C++クラスをQMLとして登録する
Register C++ classes as QML
C++ではQMLはQObjectをベースとした拡張クラスとみなされます。QObjectを継承した独自のクラスをQMLエンジンに登録することで、QMLコードからそのC++クラスを呼び出すことができます。
通常はmain()関数内のqmlファイルを読み込む前に「qmlRegisterType<
C++クラス>("
URL", "
メジャーバージョン", "
マイナーバージョン, "
QML内で使用する名称");」という形で登録します。URLとバージョンはもっぱらimport文で用いられ、URLの決まりは特にないものの、javaパッケージと同じ方式で宣言するのが定石となっています。
ためしに、HelloクラスをQMLコンポーネントとして扱えるようにしてみましょう。まずはmain.cppにHelloクラスを宣言しているヘッダファイルをインクルードし、qmlRegisterTypeでQMLエンジンに登録するようにプログラムします。
main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "hello.h"
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
qmlRegisterType<Hello>("com.tnksoft", 1, 0, "Hello");
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
return app.exec();
}
QObjectを継承したHelloクラスの作成です。QObjectをインクルードし、クラス宣言の初めにQML関連の宣言をマクロ化した「Q_OBJECT」を
セミコロンなしで記述します。
hello1.h
#ifndef HELLO_H
#define HELLO_H
#include <QObject>
class Hello : public QObject
{
Q_OBJECT
public:
Hello();
virtual ~Hello();
}
#endif
このクラスにプロパティを実装してみます。プロパティは「Q_PROPERTY」マクロを使用し、「Q_PROPERTY(
[プロパティの型] [プロパティ名] READ
[getter関数] WRITE
[setter関数] NOTIFY
[signal関数])」のような形式で、カンマでは区切らずに宣言します。READの項目は必須ですが、読み出し専用プロパティであればWRITEやNOTIFYは省略しても構いません。
型の宣言例
QML型 | プロパティ型 |
整数 | qint |
浮動小数(real) | qreal |
浮動小数(double) | qdouble |
Boolean | bool |
文字列 | QString |
日付 | QDate |
クラスの宣言部にgetter関数とsetter関数の宣言を追加します。Qtでは関数の宣言にCamel記法(先頭は小文字)を使うのが通例です。NOTIFYで記述した関数名は「signal:」として宣言し、関数の実体は作成しない点に気を付けましょう。C++で記述したシグナルは、qmakeツールによって、コンパイル先のOSに応じたイベント関数として自動で生成されます。
hello2.h
class Hello : public QObject
{
Q_OBJECT
// textという名称のプロパティを宣言
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
private:
// メンバ変数
//Qt 5.1以降ではこのようにMEMBERを用意するとgetter/setter関数が自動で作られる
//Q_PROPERTY(QString text MEMBER _text NOTIFY textChanged)
QString _text;
signals:
// シグナルは宣言のみで実装はしない
void textChanged();
public:
Hello();
virtual ~Hello();
// READで宣言した関数を実装
QString getText();
// WRITEで宣言した関数を実装
// 数値型であればconstや&は必要ない
void setText(const QString &text);
};
続いて関数の実装です。コード例のように、シグナルを発生させるには、呼び出したい箇所で「emit [シグナル関数];」を呼び出します。
hello.cpp
#include "hello.h"
#include <QDebug>
Hello::Hello() : QObject()
{
}
Hello::~Hello()
{
}
void Hello::setText(const QString &text)
{
// もしtextを使用しないのなら「Q_UNUSED(text)」をコードの先頭に記述する
_text = text;
// QMLへtext変更のシグナルを送信する
emit textChanged();
}
QString Hello::getText()
{
return _text;
}
QMLからアクセス可能な関数を実装するには、対象の関数の前に「Q_INVOKABLE」を付加します。
invoke.h
class Hello : public QObject
{
public:
Q_INVOKABLE int execCalc(int a, int b);
};
int Hello::execCalc(int a, int b)
{
return a + b;
}
QMLでの実装例です。「qmlRegisterType」で登録したURLとバージョンの組み合わせをimportして、Helloをアイテムの一部として追加します。
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
import com.tnksoft 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello Cpp")
Hello{
id:h
onTextChanged: {
// emit textChanged();によって実行される
console.log("Text has updated.")
}
}
Component.onCompleted: {
// Q_INVOKABLEの関数を実行
t.text = h.execCalc(10, 5);
}
Text{
id:t
}
TextInput{
id:i
y:10; width:100
onTextChanged: {
// プロパティへの設置により、Hello::setText()が呼び出される
h.text = this.text;
}
}
}
2018/11/05