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:
QDeclarativeView
and friends; in QML: import QtQuick 1.x
QQuickView
and friends; in QML: import QtQuick 2.x
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
)?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With