8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

QMLからC++のコンテナクラスを参照する

Posted at

QMLからC++のコンテナクラスを参照する

##要点

  • QListに変換するとQML内で配列として扱える
  • std::copyがとても便利

詳細は以下のURLから
Qt Project : Data Type Conversion Between QML and C++


###実行環境
Qt Creator 3.2.1
Qt 5.3.2


対象とするコンテナクラス

  • std::array
  • std::list
  • std::vector
  • QVector

まず, QML側にC++の拡張クラスを登録する。

main.cpp
#include <QtQml>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QObject>
#include <containertranslate.h>   //登録するC++クラスのインポート

int main(int argc, char *argv[]){
    QGuiApplication app(argc,argv);
    //QMLに登録するC++クラスの宣言
    //(engine.loadの後に宣言すると怒られる)
      qmlRegisterType<ContainerTranslate>("containertranslate",1,0,"ContainerTranslate");
    QQmlApplicationEngine engine;
    //読み込むQMLファイルの指定
    engine.load(QUrl(QStringLiteral("qrc:///main.qml")));

    return app.exec();
}


main.cppで重要なのは

  • 登録するクラスのインポート
  • qmlRegisterType を使ったクラスの登録

の2つ. qmlResisterType

qmlRegisterType<C++クラス名>("インポートファイル名",メジャーバージョン,マイナーバージョン,"QML内のエレメント宣言名")

といった具合に書く。
これをengine.load(...)の後に書くと, QML起動時に怒られるので必ず前に書こう。


次に, C++の拡張クラスを書く。
プロジェクトにファイルを追加するとき、"C++クラスを追加"を選択する。
このときに, "型情報" -> "QQuickItem" を選ぶことをお忘れなく。
これをしないとコンパイルエラーになって辛い思いをする。

これのクラスでは,

  • メンバ変数の宣言
  • QMLからのアクセッサ
    を定義する。
containertranslate.h
// /////////////// ヘッダファイル //////////////////////

#ifndef CONTAINERTRANSLATE_H
#define CONTAINERTRANSLATE_H

// -------------------------------------
//         include をお忘れなく
// -------------------------------------
#include <QQuickItem>
#include <QVector>
#include <vector>
#include <list>
#include <array>
// -------------------------------------
class ContainerTranslate : public QQuickItem
{
    Q_OBJECT          
public:
    //コンストラクタ
    explicit ContainerTranslate(QQuickItem *parent = 0);
    // C++コンテナ -> QList変換し,  QList をQMLに渡すメソッド
    //Q_INVOKABLEをつけることでQMLから呼び出し可能にする
      Q_INVOKABLE QList<qreal> getContainer();    

//-----------メンバ変数定義------------------
private:
      //検証に使うコンテナ達
      std::vector<double> vec;
      std::array<int,10> ary;
      std::list<double> list;
      QVector<double> qvec;
      //みんな最終的にはこれに変換
      QList<qreal> qlist;
};

#endif // CONTAINERTRANSLATE_H


続いてソースファイル

containertranslate.cpp
// /////////////// ソースファイル //////////////////////
#include "containertranslate.h"

ContainerTranslate::ContainerTranslate(QQuickItem *parent) :
    QQuickItem(parent)
{
    //コンテナを初期化
    for(int i=0; i<10; i++){
        vec.push_back(i);
        ary[i] = i;
        list.push_back(i);
        qvec.push_back(i);
    }
}
// C++標準コンテナをQListに変換
QList<qreal> ContainerTranslate::getContainer(){

            qlist.clear();              //メモリ解放

            //ここで std::** -> QListに変換している
            std::copy(vec.begin(),vec.end(),std::back_inserter(qlist));
//          std::copy(ary.begin(),ary.end(),std::back_inserter(qlist));
//          std::copy(list.begin(),list.end(),std::back_inserter(qlist));
//          std::copy(qvec.begin(),qvec.end(),std::back_inserter(qlist));
            return qlist;
}

ここで登場する std::copy が非常にcoolで, std::vector, list, array, QVector のすべてに対して同じ文法でコピーが行える.

x.begin() , x.end() でコンテナの先頭/末尾のイテレータを参照する.
std::back_inserter(Container &y)y の末尾に, 指定した x の要素を挿入する。

このコピーは参照渡しで動いているらしく, 複数回コピーを行っても非常に高速に動作する。


これで, QMLにコンテナを渡す機能は揃ったので, 次はQMLを書こう。

//----------------------main.qml------------------------
import QtQuick 2.0
import QtQuick.Window 2.0
import containertranslate 1.0  //ここでC++拡張クラスをインポート

Window{
        visible: true
        width: 100
        height: 62

        ContainerTranslate{id: container}  //C++拡張クラスをエレメントとして宣言

        //QMLが無事起動したときに送られるシグナル
        Component.onCompleted: {
            var ctnr = container.getContainer()  //メソッド呼び出し
            for(var i=0; i<10; i++)
                    console.debug(ctnr[i])       //コンテナの参照
        }
}

QMLからC++拡張クラスのメソッドを呼ぶときに必要なことは2つ。

  • qmlRegisterType で宣言したインポート名でクラスをインポート
  • qmlRegisterType で宣言したエレメント名でエレメントを作る。またidも必ず振る。

これさえやれば, QMLの他のエレメントメソッドを呼ぶようにC++拡張クラスのメソッドを呼び出すことができる。

上記QMLの実行結果は, ContainerTranlate::getContainer()のどの変換式を使っても

qml:0
qml:1
qml:2
qml:3
qml:4
qml:5
qml:6
qml:7
qml:8
qml:9

と表示される。


QListをQMLへメソッドで渡す場合参照渡しで渡されるため, 高速に動作する。
上記の方法を用いれば, コピーから参照までを高速に, かつ, とても簡単に実装することができる。

QMLが認識できるQListの型は int, qreal(double), bool, QUrl, QString と, 基本型を扱うならば, ほとんどがこの方法で間にあう。

もう生の配列には戻れないね .... orz

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?