QQmlApplicationEngine と QtQuick2ApplicationViewer(QQuickView) の違い

Qt を少しずつ勉強していってます。 Qt(主に Qt 4)は書籍があるので勉強しやすかったけれど、 Qt Quick(QML)は書籍もなく、がっつりまとまったサイトもないため、苦戦中。

とりあえず今日ハマってしまったところをまとめてみる。

似たような、ふたつのクラス

QML の入門的なサイトを見てみると、main 関数のあるソース(main.cpp)のなかで、QtQuick2ApplicationViewer なるクラスを使っているサイトをちらほら見かける。

ところが、私が使っている Qt Creator(バージョン 4.0.3)では、 「Qt Quick アプリケーション」を選択して新規プロジェクトを作ってみても、 QtQuick2ApplicationViewer は使用されない。 代わりに QQmlApplicationEngine クラスが使用される。

同じようなロジックのようなのだが、相違はどうなんだろうと調べてみた。
(こんなことを調べずに早く先に進みたかったのだが)

QtQuick2ApplicationViewer とは

うまく言及してくれているサイトがあった。

tips.hecomi.com

以下、ちょうどいい箇所を抜粋。

QtQuick2ApplicationViewer は Qt Creator で Qt Quick 2 プロジェクトを生成すると生成され、プラットフォーム間の違いを吸収してくれる処理が書いてある QQuickView 派生クラス(元を辿ると QWindow 派生クラス)です

API のクラス一覧にも存在しないので、なんだろうと思っていたら…そういうことかと。

その元となる QQuickView は Qt 5.0 から実装されたクラス。
http://doc.qt.io/qt-5/qquickview.html

QtQuick2ApplicationViewer とは

QQmlApplicationEngine は Qt 5.1 から実装されたクラス。
http://doc.qt.io/qt-5/qqmlapplicationengine.html

これらのクラスの違いは?

QQuickView の API ドキュメントを見ると…

The QQuickView class provides a window for displaying a Qt Quick user interface.

一方で、QtQuick2ApplicationViewer の API ドキュメントは…

Unlike QQuickView, QQmlApplicationEngine does not automatically create a root window. If you are using visual items from Qt Quick, you will need to place them inside of a Window.

QtQuick2ApplicationViewer の方には、QQuickView と対比した説明がある。 アプリケーションを表示するにあたって、ウィンドウが自動的に作られるか、そうでないかで違うとのこと。

QQuickView は自動的にウィンドウが作られる、むしろ自分自身がウィンドウなので、QML で "Window" を配置する必要がない。 一方で、QQmlApplicationEngine は自動的に作られないので、QML で "Window" を配置してあげる必要がある。

実際にソースコードを書いてみた

まず、QML を2つ用意する。

① Window なしの QML(nowrapWindow.qml)

import QtQuick 2.7

Rectangle {
    width: 640
    height: 480

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

外側に Window を配置せず、矩形(Rectangle)だけ。 QQuickView で使う想定。

② Window ありの QML(wrapWindow.qml)

import QtQuick 2.7
import QtQuick.Window 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    Text {
        text: qsTr("Hello World")
        anchors.centerIn: parent
    }
}

Window を外側に配置しておく。QQmlApplicationEngine で使う想定。

起動元となる C++ ソースコード(QQuickView)

#include <QGuiApplication>
#include <QtQuick/QQuickView>

int main(int argc, char **argv) {
    QGuiApplication app(argc, argv);

    QQuickView *view = new QQuickView;
    view->setSource(QUrl(QStringLiteral("qrc:/nowrapWindow.qml")));
    view->show();

    return app.exec();
}

「Window なしの QML」を読み込んでも、きちんとウィンドウが現れて内容が表示される。

逆に「Window ありの QML」を読み込むと、ウィンドウがふたつ現れてしまう。
ひとつは空っぽのウィンドウ(QQuickView に対応?)で、もうひとつは中身が表示されたウィンドウ(QML 内の Window に対応?)。

起動元となる C++ ソースコード(QQmlApplicationEngine)

#include <QGuiApplication>
#include <QQmlApplicationEngine>

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

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/wrapWindow.qml")));

    return app.exec();
}

QQmlApplicationEngine はウィンドウを自動的に作らないので、 「Window ありの QML」を読み込むことで、ウィンドウが現れて QQuickView と同様の表示を行うことができる。

一方で、「Window なしの QML」を読み込んだ場合、ウィンドウが作られずに何も表示されない状態になってしまう(アプリケーション自体は動いているが)。

まとめ

「(正規の API ではない)QtQuick2ApplicationViewer ってなんだ?」でハマり、時間を食ってしまったので、まとめてみた。
まとめてみたけど、わかってしまえば大したことない話…

この二つのクラス、さらに使い分けがありそうな雰囲気がするけれど、とりあえず今のところは無視しよう。

Qt と QML はやっぱり情報源が少ないのがつらいね、初心者には。