C++/QML project compatible with both Qt 4 (QtQuick 1.x) and Qt 5 (QtQuick 2.x)
c++, qml, qt, qt-quick, qtquick2
Solution
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.
Problem
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`)?