Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++/QML project compatible with both Qt 4 (QtQuick 1.x) and Qt 5 (QtQuick 2.x)

When writing a Qt application which doesn't use QML, and doesn't depend on new Qt 5 features, we can compile it with both Qt 4 and Qt 5 (except for the few source incompatibilities).

When we want to use a Qt 5 feature but want to fall back to an equivalent but less efficient Qt 4 solution, we can simply use an #if to check against the Qt version, e.g. to use the new QStringLiteral but falling back to QString::fromUtf8 when compiling with Qt 4.

How can we do the same with QtQuick? Note that it's possible to use the QDeclarativeView with QtQuick 1.x in Qt 5, but that would not use the new scene graph from Qt 5. Only 1.x is supported in QDeclarativeView and only 2.x is supported in QQuickView, even if I don't use features introduced in Quick 2.0.

What I want is:

  • When compiling with Qt 4, use QDeclarativeView and friends; in QML: import QtQuick 1.x
  • When compiling with Qt 5, use the new QQuickView and friends; in QML: import QtQuick 2.x
  • Only have a single set of QML files, but not one for QtQuick 1.x and another for QtQuick 2.x

Regarding the C++ part, this seems to be easy. In Qt 4 we can simply add:

#include <QApplication>
#include <QDeclarativeView>
#include <QDeclarativeEngine>
typedef QApplication QGuiApplication;
typedef QDeclarativeView QQuickView;

and then use QGuiApplication, QQuickView and so on in both Qt 4 and Qt 5. But when the QML file contains an import declarative for QtQuick, I can't add an #if to decide between 1.x and 2.x. Is there an official way to, let's say, add an alias to make QtQuick 1.x work in QQuickView (so it is actually parsed as QtQuick 2.0)?

like image 481
leemes Avatar asked Jan 28 '14 17:01

leemes


1 Answers

What I did was replacing the string QtQuick x.y in all QML files when deploying them. If you have a folder qml in your source tree, and want to have the same qml folder in your build tree, you can deploy the folder but replace the string to match the QtQuick version you want.

The following solution works on POSIX systems since it requires some command line tools; tested on Linux (Ubuntu). Maybe someone with experience with the Windows command line can add a version for Windows.

Add in your .pro: (The following code assumes that from within the build folder, the source folder is reachable with ../src/; if this is not the case change the path where the *** comment is)

// Define QT5 macro for use in C++, and select the correct module for QML:
greaterThan(QT_MAJOR_VERSION, 4) {
    DEFINES += QT5
    QT += quick
} else {
    QT += declarative
}

// Define qmake variables for QtQuick version, and if you want, QtQuick Controls:
equals(QT_MAJOR_VERSION, 4) {
    equals(QT_MINOR_VERSION, 7) {
        QT_QUICK_VERSION = 1.0
    }
    equals(QT_MINOR_VERSION, 8) {
        QT_QUICK_VERSION = 1.1
    }
}
equals(QT_MAJOR_VERSION, 5) {
    QT_QUICK_VERSION = 2.$${QT_MINOR_VERSION}
    equals(QT_MINOR_VERSION, 1): QT_QUICKCONTROLS_VERSION = 1.0
    equals(QT_MINOR_VERSION, 2): QT_QUICKCONTROLS_VERSION = 1.1
    equals(QT_MINOR_VERSION, 3): QT_QUICKCONTROLS_VERSION = 1.2
}

// Add a pre-build step which copies your qml folder
QtQuickVersion.target = FORCE
QtQuickVersion.commands = "rm -rf qml/;"
QtQuickVersion.commands += "cp -r ../src/qml/ .;"  // <-- *** Here is the source path
!isEmpty(QT_QUICK_VERSION) {
    QtQuickVersion.commands += "grep -rl 'QtQuick [0-9]\\.[0-9]' qml/ | xargs -r sed -i 's/QtQuick [0-9]\\.[0-9]/QtQuick $${QT_QUICK_VERSION}/g';"
}
!isEmpty(QT_QUICKCONTROLS_VERSION) {
    QtQuickVersion.commands += "grep -rl 'QtQuick.Controls [0-9]\\.[0-9]' qml/ | xargs -r sed -i 's/QtQuick.Controls [0-9]\\.[0-9]/QtQuick.Controls $${QT_QUICKCONTROLS_VERSION}/g';"
}

// Give the Makefile target *any* name which will *not* be created
// as a file, so the step is always executed
PRE_TARGETDEPS += FORCE
QMAKE_EXTRA_TARGETS += QtQuickVersion

In C++ (main.cpp), you can then create a QQuickView which falls back to QDeclarativeView for Qt 4:

#ifdef QT5

#include <QGuiApplication>
#include <QQuickView>
#include <QQmlEngine>

#else

#include <QApplication>
#include <QDeclarativeView>
#include <QDeclarativeEngine>
typedef QApplication QGuiApplication;
typedef QDeclarativeView QQuickView;
// The following is the official fallback for QStringLiteral,
// see qstring.h in Qt 5 after #ifndef QStringLiteral */
#define QStringLiteral(str) QString::fromUtf8("" str "", sizeof(str) - 1)
#endif


int main(int argc, char *argv[])
{
    QGuiApplication a(argc, argv);

    // (add qmlRegisterType etc.)

    // Open the QML view with the main QML document:
    QQuickView view;
    view.setSource(QUrl::fromLocalFile(QStringLiteral("qml/main.qml")));
    view.show();

    // Needed for "Qt.quit()" within QML:
    QObject::connect(view.engine(), SIGNAL(quit()), &a, SLOT(quit()));

    // I normally use this sizing behavior:
    view.setResizeMode(QQuickView::SizeRootObjectToView);

    return a.exec();
}

The QML file qml/main.qml opened by the code above can then look like this:

// This import will replaced with the largest supported QtQuick version:
import QtQuick 1.0

Rectangle {
    width: 450
    height: 200

    Text {
        anchors.centerIn: parent
        horizontalAlignment: Text.AlignHCenter
        font.pointSize: Math.min(parent.width / 10, parent.height / 5)

        // This text will also be replaced to show the correct QtQuick version:
        text: "Hello from\nQtQuick 1.0!"

        // Some fancy animations...
        SequentialAnimation on scale {
            running: true; loops: Animation.Infinite
            NumberAnimation { from: 1; to: .6; easing.type: Easing.InOutQuad; duration: 600 }
            NumberAnimation { from: .6; to: 1; easing.type: Easing.InOutQuad; duration: 600 }
        }
        SequentialAnimation on rotation {
            running: true; loops: Animation.Infinite
            NumberAnimation { from: -10; to: 10; easing.type: Easing.InOutQuad; duration: 2000 }
            NumberAnimation { from: 10; to: -10; easing.type: Easing.InOutQuad; duration: 2000 }
        }
    }

    MouseArea {
        anchors.fill: parent
        onClicked: Qt.quit()
    }
}

The QML file contains an import directive, which will select the proper QtQuick version (you can check this in your build folder). The string in the Text element gets replaced too, so you'll see the version easily in this demo.

like image 160
leemes Avatar answered Sep 19 '22 13:09

leemes