「QMLの基礎 - QMLとC++のバインディング」の版間の差分
ナビゲーションに移動
検索に移動
編集の要約なし |
|||
185行目: | 185行目: | ||
<br><br> | <br><br> | ||
== シグナルとスロット == | |||
以下の例では、QML側のボタン押下時にC++側へシグナルを出力してC++側のスロットで受信した後、C++側からQML側へシグナルを送出している。<br> | |||
<br> | |||
==== C++側でコネクトする ==== | |||
<u>C++側からのシグナルにおいて、引数を<code>QVariant</code>にする必要がある。</u><br> | |||
これを指定しない場合、QML側のスロットが認識されない。<br> | |||
<syntaxhighlight lang="qml"> | |||
// main.qmlファイル | |||
import QtQuick 2.5 | |||
import QtQuick.Window 2.2 | |||
import QtQuick.Controls 2.0 | |||
Window { | |||
signal qmlSignal(string msg); | |||
function qmlSlot(text) | |||
{ | |||
console.log("qmlSlot is called with the text: " + text) | |||
textField.text = text; | |||
} | |||
visible: true | |||
width: 640 | |||
height: 480 | |||
title: qsTr("Hello World") | |||
Text { | |||
id:textField | |||
text: qsTr("Hello World") | |||
anchors.centerIn: parent | |||
} | |||
Button { | |||
id:aButton | |||
text:"Emit Signal!" | |||
anchors.centerIn: parent | |||
anchors.verticalCenterOffset: 30 | |||
onClicked: qmlSignal("Hello from QML") | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cppファイル | |||
#include <QGuiApplication> | |||
#include <QQmlApplicationEngine> | |||
#include <QQmlContext> | |||
#include "CSignalSlot.h" | |||
int main(int argc, char *argv[]) | |||
{ | |||
QGuiApplication app(argc, argv); | |||
QQmlApplicationEngine engine; | |||
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); | |||
CSignalSlot SignalSlot; | |||
QObject *root = engine.rootObjects().first(); | |||
// Connect QML Signal to C++ Slot | |||
QObject::connect(root, SIGNAL(qmlSignal(QString)), &SignalSlot, SLOT(cppSlot(QString))); | |||
// Connect C++ Signal to QML Slot | |||
QObject::connect(&SignalSlot, SIGNAL(cppSignal(QVariant)), root, SLOT(qmlSlot(QVariant))); | |||
return app.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CSignalSlot.hファイル | |||
#ifndef CSIGNALSLOT_H | |||
#define CSIGNALSLOT_H | |||
#include <QObject> | |||
#include <QVariant> | |||
#include <QDebug> | |||
class CSignalSlot : public QObject | |||
{ | |||
Q_OBJECT | |||
public: | |||
explicit CSignalSlot(QObject *parent = 0) : QObject(parent) | |||
{ | |||
} | |||
signals: | |||
void cppSignal(QVariant text); | |||
public slots: | |||
void cppSlot(QString msg) | |||
{ | |||
qDebug() << "cppSlot is called with the message: " << msg; | |||
emit cppSignal("Hello from C++"); | |||
} | |||
}; | |||
#endif // CSIGNALSLOT_H | |||
</syntaxhighlight> | |||
<br> | |||
==== QML側でコネクトする ==== | |||
C++側からシグナル(cppSignalメソッド)を送出する時の動作をonCppSignalと記述する。<br> | |||
<u>これは、C++側はLower Camelで記述、QML側は"on" + Upper Camelで記述する必要がある。</u><br> | |||
<br> | |||
main.cppにおいて、ルートオブジェクト(<code>rootContext</code>クラス)のコンテキスト(<code>setContextProperty</code>メソッド)を設定する前にmain.qmlを読み込むと、<br> | |||
以下のエラーが出力されるため注意すること。<br> | |||
QML Connections: Cannot assign to non-existent property "OnCppSignal" | |||
ReferenceError: cppSignalSlot is not defined | |||
<br> | |||
<syntaxhighlight lang="qml"> | |||
// main.qmlファイル | |||
import QtQuick 2.5 | |||
import QtQuick.Window 2.2 | |||
import QtQuick.Controls 2.0 | |||
Window { | |||
visible: true | |||
width: 640 | |||
height: 480 | |||
title: qsTr("Hello World") | |||
Text { | |||
id: textField | |||
text: qsTr("Hello World") | |||
anchors.centerIn: parent | |||
} | |||
Button { | |||
text:"Emit Signal!" | |||
anchors.centerIn: parent | |||
anchors.verticalCenterOffset: 30 | |||
onClicked: cSignalSlot.cppSlot("Hello from QML") | |||
} | |||
Connections { | |||
target:cSignalSlot | |||
onCppSignal: { | |||
console.log("received cppSignal:" + text) | |||
textField.text = text; | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cppファイル | |||
#include <QGuiApplication> | |||
#include <QQmlApplicationEngine> | |||
#include <QQmlContext> | |||
#include "CSignalSlot.h" | |||
int main(int argc, char *argv[]) | |||
{ | |||
QGuiApplication app(argc, argv); | |||
QQmlApplicationEngine engine; | |||
CSignalSlot SignalSlot; | |||
// Put this before lading qml file! | |||
engine.rootContext()->setContextProperty("CSignalSlot", &SignalSlot); | |||
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); | |||
return app.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// CSignalSlot.hファイル | |||
#ifndef CSIGNALSLOT_H | |||
#define CSIGNALSLOT_H | |||
#include <QObject> | |||
#include <QDebug> | |||
class CSignalSlot : public QObject | |||
{ | |||
Q_OBJECT | |||
public: | |||
explicit CSignalSlot(QObject *parent = 0) : QObject(parent) | |||
{ | |||
} | |||
signals: | |||
void cppSignal(QString text); | |||
public slots: | |||
void cppSlot(QString text) | |||
{ | |||
qDebug() << "cppSlot is called with text: " + text; | |||
emit cppSignal(QString("Hello from C++")); | |||
} | |||
}; | |||
#endif // CSIGNALSLOT_H | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] |
2021年8月31日 (火) 04:07時点における最新版
概要
QMLとC++のバインディング手順を記載する。
ここでは、Qt Creatorにおいて、Qt Quickの空のプロジェクトを作成した時に自動生成されるソースコードを元にする。
QMLからC++へアクセスする (推奨)
setContextProperty
メソッドにQObject
のインスタンスを渡して、QMLからC++のクラスメソッドを呼んでいる。
// main.cppファイル
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "CMainWindow.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
[url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
// 追加
CMainWindow MainWindow;
engine.rootContext()->setContextProperty("MainWindow", &MainWindow);
// 複数のクラスを追加する場合、以下のように記述する
//CSubWindow SubWindow;
//engine.rootContext()->setContextProperty("SubWindow", &SubWindow);
return app.exec();
}
// CMainWindow.hファイル
#pragma once
#include <QObject>
#include <QString>
class CMainWindow : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE QString getTextFromCpp()
{
return QString("This is the text from C++");
}
};
// main.qmlファイル
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
// 追加
Text {
id: textMainMessage
text: qsTr("ここにテキストが表示される")
x: (parent.width / 2) - width / 2
y: (parent.height / 2) - height / 2
Layout.fillWidth: true
Layout.fillHeight: true
font.pixelSize: 15
color: "#ffffff"
}
Button {
id: mainWindowButton
x: (parent.width / 2) - width / 2
y: (parent.height / 2) + height / 2
width: 120
height: 35
Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter
text: qsTr("OK")
Connections {
target: mainWindowButton
function onClicked() {
let strMainMessage = MainWindow.getTextFromCpp(); // C++のメソッドを呼ぶ
textMainMessage.text = strMainMessage;
}
}
}
}
C++からQMLオブジェクトへアクセスする (非推奨)
C++からQMLオブジェクトへのアクセスは、C++とQMLの独立性の観点から非推奨であることに注意する。
ここでは、objectNameでアクセスする方法を記載する。(他にも様々な手順がある)
まず、root要素へアクセスして、子要素であるText
へQMLで定義するobjectName
でアクセスする。
// main.cppファイル
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "CMainWindow.h"
int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
[url](QObject *obj, const QUrl &objUrl)
{
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
// 追加
// ルートオブジェクトおよびmain.qmlに定義されているobjectName(textObject)のQMLオブジェクトへのアクセスを取得する
QObject *rootObject = engine.rootObjects().first();
QObject *qmlObject = rootObject->findChild<QObject*>("textObject");
// QMLオブジェクトに対して、任意の値を設定する
qmlObject->setProperty("text", "Text from C++");
return app.exec();
}
次に、QML側では、アクセスする子要素にobjectName
を定義する。
// main.qmlファイル
import QtQuick 2.5
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
// 追加
Text {
id: textMainMessage
objectName: "textObject"
text: qsTr("ここにテキストが表示される")
x: (parent.width / 2) - width / 2
y: (parent.height / 2) - height / 2
Layout.fillWidth: true
Layout.fillHeight: true
font.pixelSize: 15
color: "#ffffff"
}
}
シグナルとスロット
以下の例では、QML側のボタン押下時にC++側へシグナルを出力してC++側のスロットで受信した後、C++側からQML側へシグナルを送出している。
C++側でコネクトする
C++側からのシグナルにおいて、引数をQVariant
にする必要がある。
これを指定しない場合、QML側のスロットが認識されない。
// main.qmlファイル
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
signal qmlSignal(string msg);
function qmlSlot(text)
{
console.log("qmlSlot is called with the text: " + text)
textField.text = text;
}
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Text {
id:textField
text: qsTr("Hello World")
anchors.centerIn: parent
}
Button {
id:aButton
text:"Emit Signal!"
anchors.centerIn: parent
anchors.verticalCenterOffset: 30
onClicked: qmlSignal("Hello from QML")
}
}
// main.cppファイル
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "CSignalSlot.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
CSignalSlot SignalSlot;
QObject *root = engine.rootObjects().first();
// Connect QML Signal to C++ Slot
QObject::connect(root, SIGNAL(qmlSignal(QString)), &SignalSlot, SLOT(cppSlot(QString)));
// Connect C++ Signal to QML Slot
QObject::connect(&SignalSlot, SIGNAL(cppSignal(QVariant)), root, SLOT(qmlSlot(QVariant)));
return app.exec();
}
// CSignalSlot.hファイル
#ifndef CSIGNALSLOT_H
#define CSIGNALSLOT_H
#include <QObject>
#include <QVariant>
#include <QDebug>
class CSignalSlot : public QObject
{
Q_OBJECT
public:
explicit CSignalSlot(QObject *parent = 0) : QObject(parent)
{
}
signals:
void cppSignal(QVariant text);
public slots:
void cppSlot(QString msg)
{
qDebug() << "cppSlot is called with the message: " << msg;
emit cppSignal("Hello from C++");
}
};
#endif // CSIGNALSLOT_H
QML側でコネクトする
C++側からシグナル(cppSignalメソッド)を送出する時の動作をonCppSignalと記述する。
これは、C++側はLower Camelで記述、QML側は"on" + Upper Camelで記述する必要がある。
main.cppにおいて、ルートオブジェクト(rootContext
クラス)のコンテキスト(setContextProperty
メソッド)を設定する前にmain.qmlを読み込むと、
以下のエラーが出力されるため注意すること。
QML Connections: Cannot assign to non-existent property "OnCppSignal" ReferenceError: cppSignalSlot is not defined
// main.qmlファイル
import QtQuick 2.5
import QtQuick.Window 2.2
import QtQuick.Controls 2.0
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Text {
id: textField
text: qsTr("Hello World")
anchors.centerIn: parent
}
Button {
text:"Emit Signal!"
anchors.centerIn: parent
anchors.verticalCenterOffset: 30
onClicked: cSignalSlot.cppSlot("Hello from QML")
}
Connections {
target:cSignalSlot
onCppSignal: {
console.log("received cppSignal:" + text)
textField.text = text;
}
}
}
// main.cppファイル
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "CSignalSlot.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
CSignalSlot SignalSlot;
// Put this before lading qml file!
engine.rootContext()->setContextProperty("CSignalSlot", &SignalSlot);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
// CSignalSlot.hファイル
#ifndef CSIGNALSLOT_H
#define CSIGNALSLOT_H
#include <QObject>
#include <QDebug>
class CSignalSlot : public QObject
{
Q_OBJECT
public:
explicit CSignalSlot(QObject *parent = 0) : QObject(parent)
{
}
signals:
void cppSignal(QString text);
public slots:
void cppSlot(QString text)
{
qDebug() << "cppSlot is called with text: " + text;
emit cppSignal(QString("Hello from C++"));
}
};
#endif // CSIGNALSLOT_H