読者です 読者をやめる 読者になる 読者になる

凹みTips

C++、JavaScript、Unity、ガジェット等の Tips について雑多に書いています。

QML で特定のフォルダ内に含まれる画像をスライドショーする処理を書いてみた

C++ JavaScript Qt

はじめに

QML は JavaScript ベースなので、ファイルを扱うことができません(XHR とかは出来ます)。なので、特定のディレクトリに含まれる画像をスライドショーしたいなぁ、と思っても、画像パスのリストを JS から取得することが出来ず、ファイル名を JS に配列で列挙する、という形になってしまいます。そこでこれを解消するためには、C++ でそのリストを作成して上げて結果を QML 側に渡してあげる、という処理が必要となります。入用になって調べてみたのでメモします。

環境

  • Mac OS X 10.8.3
  • Qt 5.0.1 / Qt Creator 2.6.2 で QtQuick1 のプロジェクトを作成

(QtQuick2 使いたかったけど情報少ない...)

QML

import QtQuick 1.1

Rectangle {
    id: window
    width: 360
    height: 360

    property string  picDirPath : "/Path/to/your/picture/directory"
    property variant picPaths   : []
    property int     picIndex   : 0

    Image {
        id: picture
        width: parent.width
        height: picture.width * 0.75
    }

    Timer {
        id: timer
        running: true
        interval: 30
        repeat: true
        onTriggered: {
            if (window.picIndex >= window.picPaths.length) {
                window.picIndex = 0;
            }
            picture.source = window.picPaths[window.picIndex++];
        }
    }

    function onReadDir(fileNames) {
        var list = [];
        fileNames.forEach(function(fileName) {
            if (fileName.match(/jpg$/i) !== null) {
                list.push(fileName);
            }
        });
        window.picPaths = list;
    }
}

この picDirPath プロパティを C++ で取得して、そのディレクトリ内の画像を読み込み、onReadDir の引数に結果を流し込む、という流れになります。

C++

#include <QApplication>
#include <QGraphicsObject>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>
#include "qmlapplicationviewer.h"

namespace fs = boost::filesystem;

Q_DECL_EXPORT int main(int argc, char *argv[])
{
    QScopedPointer<QApplication> app(new QApplication(argc, argv));

    // QML から GUI を作成 --> フルスクリーンで表示
    QmlApplicationViewer viewer;
    viewer.setOrientation(QmlApplicationViewer::ScreenOrientationAuto);
    viewer.setMainQmlFile(QLatin1String("qml/SlideShow/main.qml"));
    viewer.showFullScreen();

    // ルート要素を取得
    QGraphicsObject* root_obj = viewer.rootObject();

    // 画像を格納したフォルダのパスを QML から読み出す
    QString pic_dir_path = root_obj->property("picDirPath").toString();

    // 特定のフォルダ内のファイル名を洗い出す
    QVariantList list;
    const fs::path path(pic_dir_path.toStdString());
    BOOST_FOREACH(const fs::path& p,
                  std::make_pair(fs::directory_iterator(path),
                                 fs::directory_iterator())) {
        if (!fs::is_directory(p)) {
            list << p.c_str();
        }
    }

    // QML 内で定義した関数を呼び出す
    QMetaObject::invokeMethod(root_obj, "onReadDir", Q_ARG(QVariant, list));

    return app->exec();
}

QML 内のプロパティは以下のようにして取得出来ます。

viewer.rootObject()->property("hogehoge")

あとは toHoge() をしてあげれば、所望の型で変数が取得出来ます。
逆に QML 側の関数を呼ぶ際は以下のようにします。

QMetaObject::invokeMethod(ルートオブジェクト, "QML 側の関数名", Q_ARG(型名, 引数に与える変数), ...);

とても簡単で素晴らしい。。

.pro

Boost.Filesystem を使ったので以下の記述を追加。

LIBS += -lboost_filesystem -lboost_system

おわりに

v8 で JS バインディング書くより楽な気がします。クラスのバインディングも簡単そうなので次回やってみます。