📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)
| (同じ利用者による、間の8版が非表示) | |||
| 22行目: | 22行目: | ||
== QML_SINGLETONマクロ == | == QML_SINGLETONマクロ == | ||
==== QML_SINGLETONマクロとは ==== | |||
<code>QML_SINGLETON</code>マクロは、QML環境において、C++クラスをQMLシングルトンとして露出させるために使用する。<br> | |||
<code>QML_SINGLETON</code>マクロを使用することにより、QMLコードからグローバルに利用可能な単一のインスタンスを生成することができる。<br> | |||
<br> | |||
主な目的は、アプリケーション全体で共有される状態やサービスを提供することである。<br> | |||
例えば、設定管理、ログ記録、ネットワーク接続管理等のグローバルサービスを実装する場合に非常に有効である。<br> | |||
<br> | |||
QMLにおいて、内包する型がシングルトンであることを宣言するには、<code>QObject</code>クラスを継承したクラスを記述して、<code>QML_SINGLETON</code>マクロを使用する。<br> | QMLにおいて、内包する型がシングルトンであることを宣言するには、<code>QObject</code>クラスを継承したクラスを記述して、<code>QML_SINGLETON</code>マクロを使用する。<br> | ||
これは、そのクラスが<code>Q_OBJECT</code>マクロが記述されており、QMLで利用可能である場合(<code>QML_ELEMENT</code>マクロまたは<code>QML_NAMED_ELEMENT()</code>マクロを持つ場合)にのみ有効である。<br> | |||
<br> | <br> | ||
* Qt 5の場合 | |||
*: 各<code>QQmlEngine</code>クラスは、その型に最初にアクセスした時、その型のデフォルトコンストラクタを使用してシングルトンのインスタンスを生成する。 | |||
*: もし、デフォルトコンストラクタが存在しない場合は、シングルトンは初期状態ではアクセス不能になるため、以下に示す記述を行う。 | |||
** <code>qmlRegisterSingletonType</code>関数に特定のファクトリー関数を指定する。 | |||
** <code>qmlRegisterSingletonInstance</code>関数に同じクラスで同じ型の名前空間とバージョンの特定のインスタンスを指定して呼び出すことにより、オーバーライドする。 | |||
<br> | <br> | ||
* Qt 6の場合 | |||
*: 以下に示すタイミングで、シングルトンのインスタンスが生成される。 | |||
<code> | ** 各<code>QQmlEngine</code>クラスは、その型に最初にアクセスした時、その型のデフォルトコンストラクタが使用される。 | ||
< | ** デフォルトコンストラクタが存在しない場合は、静的ファクトリー関数(<code>T *create(QQmlEngine*, QJSEngine*)</code>)が使用される。 | ||
*: <br> | |||
*: もし、両方が存在して両方アクセス可能な場合は、デフォルトコンストラクタが優先される。 | |||
*: <u>ただし、デフォルトコンストラクタも静的ファクトリ関数も存在しない場合は、シングルトンはアクセス不能になることに注意する。</u> | |||
<br> | <br> | ||
QMLファイル内でシングルトンを使用する場合は、通常のQMLタイプと同様にインポートする。<br> | |||
インスタンス化する必要はなく、直接そのプロパティやメソッドにアクセスすることができる。<br> | |||
<br> | <br> | ||
<u>※注意</u><br> | |||
<u><code>QML_SINGLETON</code>マクロを使用する場合は、クラスが<code>Q_OBJECT</code>マクロも使用している必要がある。</u><br> | |||
<u>また、シングルトンクラスは少なくとも1つの<code>Q_INVOKABLE</code>メソッド、または、<code>NOTIFIABLE</code>プロパティを持っている必要がある。</u><br> | |||
<br> | <br> | ||
デフォルトコンストラクタを持つクラスをシングルトンとして宣言する場合は、<code>QML_SINGLETON</code> | ==== デフォルトコンストラクタが存在する場合 ==== | ||
デフォルトコンストラクタを持つクラスをシングルトンとして宣言する場合は、<code>QML_SINGLETON</code>マクロを記述する。<br> | |||
<br> | <br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
| 49行目: | 66行目: | ||
public: | public: | ||
// メンバ変数 | // メンバ変数, メンバ関数, Q_INVOKABLEメソッド等 | ||
}; | }; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== 静的ファクトリー関数が存在する場合 ==== | |||
シングルトンクラスがデフォルトで構築不可であり修正可能な場合は、アクセス可能にするため、静的ファクトリー関数を追加する。<br> | シングルトンクラスがデフォルトで構築不可であり修正可能な場合は、アクセス可能にするため、静的ファクトリー関数を追加する。<br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
| 76行目: | 94行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
==== 両方存在しない場合 ==== | |||
シングルトンクラスが変更不可で、デフォルトコンストラクタや静的ファクトリー関数が存在しない場合、<br> | |||
QML_FOREIGNラッパーを使用して、静的ファクトリー関数を定義することが可能である。<br> | |||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
struct SingletonForeign | struct SingletonForeign | ||
| 150行目: | 170行目: | ||
<u>上記で示したように、<code>create()</code>メソッドのパラメータでエンジンのIDやスレッドアフィニティを確認することにより、アサーションすることができる。</u><br> | <u>上記で示したように、<code>create()</code>メソッドのパラメータでエンジンのIDやスレッドアフィニティを確認することにより、アサーションすることができる。</u><br> | ||
<br><br> | <br><br> | ||
== QML_SINGLETONを使用する場合 == | |||
==== 使用例 : デフォルトコンストラクタを使用する場合 ==== | |||
以下の例では、QML_SINGLETONマクロを使用してConfigServiceをシングルトンクラスとして定義して、アプリケーションのテーマとダークモードの設定を管理している。<br> | |||
<br> | |||
C++側では、main関数内でqmlRegisterSingletonType関数を使用して、このシングルトンをQMLエンジンに登録している。<br> | |||
<br> | |||
QML側では、ConfigServiceシングルトンを直接使用して、現在の設定の表示やユーザの操作に応じて設定を変更している。<br> | |||
これにより、アプリケーション全体で一貫した設定管理が可能になり、C++とQML間でシームレスにデータを共有することができる。<br> | |||
<br> | |||
シングルトンの使用により、コードの重複を避けてアプリケーション全体で一貫したステート管理を実現できる。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// ConfigService.hファイル | |||
#include <QObject> | |||
#include <QQmlEngine> | |||
#include <QSettings> | |||
class ConfigService : public QObject | |||
{ | |||
Q_OBJECT | |||
QML_SINGLETON | |||
Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged) | |||
Q_PROPERTY(bool darkMode READ darkMode WRITE setDarkMode NOTIFY darkModeChanged) | |||
private: | |||
QString m_theme; | |||
bool m_darkMode; | |||
public: | |||
explicit ConfigService(QObject *parent) : QObject(parent), m_theme("Default"), m_darkMode(false) | |||
{ | |||
loadSettings(); | |||
} | |||
QString theme() const | |||
{ | |||
return m_theme; | |||
} | |||
void setTheme(const QString &theme) | |||
{ | |||
if (m_theme != theme) { | |||
m_theme = theme; | |||
emit themeChanged(); | |||
} | |||
} | |||
bool darkMode() const | |||
{ | |||
return m_darkMode; | |||
} | |||
void setDarkMode(bool darkMode) | |||
{ | |||
if (m_darkMode != darkMode) { | |||
m_darkMode = darkMode; | |||
emit darkModeChanged(); | |||
} | |||
} | |||
Q_INVOKABLE void saveSettings() | |||
{ | |||
QSettings settings("MyCompany", "MyApp"); | |||
settings.setValue("theme", m_theme); | |||
settings.setValue("darkMode", m_darkMode); | |||
} | |||
Q_INVOKABLE void loadSettings() | |||
{ | |||
QSettings settings("MyCompany", "MyApp"); | |||
setTheme(settings.value("theme", "Default").toString()); | |||
setDarkMode(settings.value("darkMode", false).toBool()); | |||
} | |||
signals: | |||
void themeChanged(); | |||
void darkModeChanged(); | |||
}; | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cppファイル | |||
#include <QGuiApplication> | |||
#include <QQmlApplicationEngine> | |||
#include "ConfigService.h" | |||
int main(int argc, char *argv[]) | |||
{ | |||
QGuiApplication app(argc, argv); | |||
qmlRegisterSingletonType<ConfigService>("com.mycompany.configservice", 1, 0, "ConfigService", | |||
[](QQmlEngine *engine, QJSEngine *scriptEngine) -> QObject * { | |||
Q_UNUSED(engine) | |||
Q_UNUSED(scriptEngine) | |||
return new ConfigService(); | |||
}); | |||
QQmlApplicationEngine engine; | |||
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); | |||
return app.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="qml"> | |||
// main.qmlファイル | |||
import QtQuick | |||
import QtQuick.Controls | |||
import com.mycompany.configservice 1.0 | |||
ApplicationWindow { | |||
visible: true | |||
width: 640 | |||
height: 480 | |||
title: qsTr("Config Service Example") | |||
Column { | |||
anchors.centerIn: parent | |||
spacing: 10 | |||
Text { | |||
text: "Current Theme: " + ConfigService.theme | |||
} | |||
Text { | |||
text: "Dark Mode: " + (ConfigService.darkMode ? "On" : "Off") | |||
} | |||
Button { | |||
text: "Toggle Dark Mode" | |||
onClicked: { | |||
ConfigService.darkMode = !ConfigService.darkMode | |||
ConfigService.saveSettings() | |||
} | |||
} | |||
ComboBox { | |||
model: ["Default", "Light", "Dark"] | |||
onCurrentTextChanged: { | |||
ConfigService.theme = currentText | |||
ConfigService.saveSettings() | |||
} | |||
} | |||
} | |||
Component.onCompleted: { | |||
ConfigService.loadSettings() | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
==== 使用例 : 静的ファクトリー関数を使用する場合 ==== | |||
QML_SINGLETONマクロは、C++クラスをQMLシングルトンとして登録するために使用する。<br> | |||
<br> | |||
デフォルトコンストラクタが存在せず、静的ファクトリー関数が存在する場合、<br> | |||
まず、クラス宣言において、QML_SINGLETONマクロを使用する。<br> | |||
これは通常、クラスのpublicセクションに配置する。<br> | |||
<br> | |||
次に、静的ファクトリー関数を定義する。<br> | |||
この関数は、シングルトンインスタンスを作成して返す役割を持つ。<br> | |||
以下の例では、createSingletonという名前の関数を定義している。<br> | |||
<br> | |||
<code>QML_SINGLETON</code>マクロの引数には、この静的ファクトリー関数を指定する。<br> | |||
つまり、<code>QML_SINGLETON(<シングルトンクラス名>, <静的ファクトリー関数名>)</code>のような形式となる。<br> | |||
例えば、<u>QML_SINGLETON(MySingleton, createSingleton)</u>となる。<br> | |||
<br> | |||
実装する場合には、静的メンバ変数を使用してシングルトンインスタンスを保持して、静的ファクトリー関数内でこのインスタンスを生成・返却する方法が一般的である。<br> | |||
<br> | |||
QMLエンジンがシングルトンを必要とするまで、実際のインスタンス生成を遅延させることができる。<br> | |||
これにより、リソースの効率的な利用が可能になる。<br> | |||
<br> | |||
<u>※注意</u><br> | |||
<u>スレッドセーフティに関しても考慮が必要である。</u><br> | |||
<u>複数のスレッドから同時にアクセスされる可能性がある場合、適切な同期メカニズムを実装することが重要となる。</u><br> | |||
<br> | |||
静的ファクトリー関数を使用することにより、デフォルトコンストラクタを持たないクラスでも、QMLシングルトンとして効果的に機能させることができる。<br> | |||
静的ファクトリー関数を通じて、インスタンスの生成・返却を細かく制御できるため、より柔軟性の高い設計が可能になる。<br> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// MySingleton.hファイル | |||
#include <QObject> | |||
#include <QtQml/qqmlregistration.h> | |||
#include <QMutex> | |||
#include <QMutexLocker> | |||
#include <memory> | |||
#include <QDebug> | |||
class MySingleton : public QObject | |||
{ | |||
Q_OBJECT | |||
QML_SINGLETON(MySingleton, createSingleton) | |||
private: | |||
explicit MySingleton(QObject *parent = nullptr) : QObject(parent) {} | |||
static std::unique_ptr<MySingleton> instance; // インスタンスの生存期間を適切に管理する | |||
QMutex m_mutex; | |||
// コピーコンストラクタと代入演算子の無効化 | |||
MySingleton(const MySingleton&) = delete; | |||
MySingleton& operator=(const MySingleton&) = delete; | |||
public: | |||
Q_INVOKABLE void doSomething() | |||
{ | |||
// このメソッドが複数のスレッドから同時に呼ばれても、安全に実行される | |||
QMutexLocker locker(&m_mutex); | |||
qDebug() << "MySingleton is doing something!"; | |||
} | |||
static MySingleton *createSingleton(QQmlEngine *engine, QJSEngine *scriptEngine) | |||
{ | |||
Q_UNUSED(engine) | |||
Q_UNUSED(scriptEngine) | |||
// 複数のスレッドから同時に呼ばれても、インスタンスが1度だけ生成されることを保証する | |||
static QMutex mutex; | |||
QMutexLocker locker(&mutex); | |||
if (!instance) { | |||
// インスタンスの生存期間を適切に管理する | |||
instance.reset(new MySingleton()); | |||
} | |||
return instance.get(); | |||
} | |||
}; | |||
// メモリリークを防いで、リソース管理を改善する | |||
std::unique_ptr<MySingleton> MySingleton::instance = nullptr; | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cppファイル | |||
#include <QGuiApplication> | |||
#include <QQmlApplicationEngine> | |||
#include "MySingleton.h" | |||
int main(int argc, char *argv[]) | |||
{ | |||
QGuiApplication app(argc, argv); | |||
// Qt 6では、QMLエンジンが起動時に自動的にタイプを検出・登録する | |||
// そのため、qmlRegisterSingletonType関数を使用する必要はない | |||
// この自動登録メカニズムは、プロジェクトが適切に設定されている場合にのみ機能する | |||
QQmlApplicationEngine engine; | |||
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); | |||
return app.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="qml"> | |||
// main.qmlファイル | |||
import QtQuick | |||
import QtQuick.Window | |||
Window { | |||
width: 640 | |||
height: 480 | |||
visible: true | |||
title: qsTr("QML Singleton Example") | |||
Text { | |||
anchors.centerIn: parent | |||
text: "Click me!" | |||
font.pixelSize: 24 | |||
MouseArea { | |||
anchors.fill: parent | |||
onClicked: { | |||
MySingleton.doSomething() | |||
} | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
== QML_SINGLETONを使用しない場合 == | |||
==== 使用例: 複数のQMLから同一のシングルトンクラスを参照する ==== | |||
以下の例では、2つの画面 (Screen1.qmlとScreen2.qml) から同一のシングルトンクラスを参照する。<br> | |||
<br> | |||
<u>これにより、複数のQMLファイルから同一のシングルトンインスタンスにアクセスすることができる。</u><br> | |||
<u>いずれかの画面にてシングルトンインスタンスのメンバ変数を変更した場合でも、複数の画面に反映される。</u><br> | |||
<br> | |||
このアプローチを使用することにより、複数のQMLファイルから同一のC++シングルトンクラスにアクセスして、データを共有することができる。<br> | |||
<br> | |||
まず、C++でシングルトンクラスを定義および生成して、QMLエンジンに登録する必要があるため、<br> | |||
C++でシングルトンクラスを定義する。<br> | |||
<syntaxhighlight lang="c++"> | |||
// hoge.cpp | |||
#include "hoge.h" | |||
Hoge::Hoge(QObject *parent) : QObject(parent), m_message("Hello from Hoge!") | |||
{ | |||
} | |||
Hoge& Hoge::getInstance() | |||
{ | |||
static Hoge instance; | |||
return instance; | |||
} | |||
QString Hoge::message() const | |||
{ | |||
return m_message; | |||
} | |||
void Hoge::setMessage(const QString &message) | |||
{ | |||
if (m_message != message) { | |||
m_message = message; | |||
emit messageChanged(); | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="c++"> | |||
// hoge.h | |||
#ifndef HOGE_H | |||
#define HOGE_H | |||
#include <QObject> | |||
class Hoge : public QObject | |||
{ | |||
Q_OBJECT | |||
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged) | |||
private: | |||
QString m_message; | |||
private: | |||
Hoge(QObject *parent = nullptr); | |||
~Hoge() = default; | |||
Hoge(const Hoge&) = delete; | |||
Hoge& operator=(const Hoge&) = delete; | |||
public: | |||
static Hoge& getInstance(); | |||
QString message() const; | |||
void setMessage(const QString &message); | |||
signals: | |||
void messageChanged(); | |||
}; | |||
#endif | |||
</syntaxhighlight> | |||
<br> | |||
次に、main関数において、QMLエンジンにシングルトンクラスを登録する。<br> | |||
<syntaxhighlight lang="c++"> | |||
// main.cpp | |||
#include <QGuiApplication> | |||
#include <QQmlApplicationEngine> | |||
#include <QQmlContext> | |||
#include "hoge.h" | |||
int main(int argc, char *argv[]) | |||
{ | |||
QGuiApplication app(argc, argv); | |||
QQmlApplicationEngine engine; | |||
// シングルトンクラスをQMLに登録 | |||
engine.rootContext()->setContextProperty("hoge", &Hoge::getInstance()); | |||
engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); | |||
return app.exec(); | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
複数のQMLファイルからシングルトンクラスを使用する。<br> | |||
<syntaxhighlight lang="qml"> | |||
// Screen1.qml | |||
import QtQuick | |||
import QtQuick.Controls | |||
Item { | |||
width: 300 | |||
height: 200 | |||
Text { | |||
anchors.centerIn: parent | |||
text: hoge.message | |||
} | |||
Button { | |||
anchors.top: parent.top | |||
anchors.horizontalCenter: parent.horizontalCenter | |||
text: "Set Message from Screen1" | |||
onClicked: hoge.setMessage("Message set from Screen1") | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
<syntaxhighlight lang="qml"> | |||
// Screen2.qml | |||
import QtQuick | |||
import QtQuick.Controls | |||
Item { | |||
width: 300 | |||
height: 200 | |||
Text { | |||
anchors.centerIn: parent | |||
text: hoge.message | |||
} | |||
Button { | |||
anchors.bottom: parent.bottom | |||
anchors.horizontalCenter: parent.horizontalCenter | |||
text: "Set Message from Screen2" | |||
onClicked: hoge.setMessage("Message set from Screen2") | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br> | |||
最後に、メイン画面 (main.qml) において、上記の2つの画面を表示する。<br> | |||
<syntaxhighlight lang="qml"> | |||
// main.qml | |||
import QtQuick | |||
import QtQuick.Window | |||
Window { | |||
width: 600 | |||
height: 400 | |||
visible: true | |||
title: qsTr("Hoge Singleton Example") | |||
Row { | |||
anchors.fill: parent | |||
Screen1 { | |||
width: parent.width / 2 | |||
height: parent.height | |||
} | |||
Screen2 { | |||
width: parent.width / 2 | |||
height: parent.height | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<br><br> | |||
__FORCETOC__ | __FORCETOC__ | ||
[[カテゴリ:Qt]] | [[カテゴリ:Qt]] | ||