MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
QMLの基礎 - シングルトンのソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
QMLの基礎 - シングルトン
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == オブジェクト指向のプログラム言語のクラスのデザインパターンの1種である。<br> デザインパターンとは、オブジェクト指向の言語で使用される設計のパターンのことであり、設計時に直面する問題とその解決策を整理してまとめたものを指す。<br> <br> シングルトンは、クラスの設計方法の1つであり、クラスのインスタンスを2つ以上生成できないようにすることで、常に同一のインスタンスが参照されるデザインパターンとなる。<br> 例えば、インスタンスの状態を常に持つ場合やクラス端で共通のデータとしてアクセスする必要がある場合に使用される。<br> <br><br> == 前提条件 == QQmlEngineヘッダファイルをインクルードする必要がある。<br> また、qmakeを使用する場合は、変数QTにqmlを追加する必要がある。<br> <syntaxhighlight lang="c++"> // cppファイル #include <QQmlEngine> </syntaxhighlight> <br> # qmakeファイル QT += qml <br><br> == 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> これは、そのクラスが<code>Q_OBJECT</code>マクロが記述されており、QMLで利用可能である場合(<code>QML_ELEMENT</code>マクロまたは<code>QML_NAMED_ELEMENT()</code>マクロを持つ場合)にのみ有効である。<br> <br> * Qt 5の場合 *: 各<code>QQmlEngine</code>クラスは、その型に最初にアクセスした時、その型のデフォルトコンストラクタを使用してシングルトンのインスタンスを生成する。 *: もし、デフォルトコンストラクタが存在しない場合は、シングルトンは初期状態ではアクセス不能になるため、以下に示す記述を行う。 ** <code>qmlRegisterSingletonType</code>関数に特定のファクトリー関数を指定する。 ** <code>qmlRegisterSingletonInstance</code>関数に同じクラスで同じ型の名前空間とバージョンの特定のインスタンスを指定して呼び出すことにより、オーバーライドする。 <br> * Qt 6の場合 *: 以下に示すタイミングで、シングルトンのインスタンスが生成される。 ** 各<code>QQmlEngine</code>クラスは、その型に最初にアクセスした時、その型のデフォルトコンストラクタが使用される。 ** デフォルトコンストラクタが存在しない場合は、静的ファクトリー関数(<code>T *create(QQmlEngine*, QJSEngine*)</code>)が使用される。 *: <br> *: もし、両方が存在して両方アクセス可能な場合は、デフォルトコンストラクタが優先される。 *: <u>ただし、デフォルトコンストラクタも静的ファクトリ関数も存在しない場合は、シングルトンはアクセス不能になることに注意する。</u> <br> QMLファイル内でシングルトンを使用する場合は、通常のQMLタイプと同様にインポートする。<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> ==== デフォルトコンストラクタが存在する場合 ==== デフォルトコンストラクタを持つクラスをシングルトンとして宣言する場合は、<code>QML_SINGLETON</code>マクロを記述する。<br> <br> <syntaxhighlight lang="c++"> class MySingleton : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON // Q_PROPERTY( ... ) public: // メンバ変数, メンバ関数, Q_INVOKABLEメソッド等 }; </syntaxhighlight> <br> ==== 静的ファクトリー関数が存在する場合 ==== シングルトンクラスがデフォルトで構築不可であり修正可能な場合は、アクセス可能にするため、静的ファクトリー関数を追加する。<br> <syntaxhighlight lang="c++"> class MySingleton : public QObject { Q_OBJECT QML_ELEMENT QML_SINGLETON // Q_PROPERTY( ... ) public: static MySingleton *create(QQmlEngine *qmlEngine, QJSEngine *jsEngine) { MySingleton *result = nullptr; // 何らかのカスタムコンストラクタやファクトリを用いてオブジェクトを生成する // QMLエンジンは、最終的にこのオブジェクトの所有権を取得および削除する return result; } // メンバ変数 / メンバ関数, Q_INVOKABLEメソッド等 }; </syntaxhighlight> <br> ==== 両方存在しない場合 ==== シングルトンクラスが変更不可で、デフォルトコンストラクタや静的ファクトリー関数が存在しない場合、<br> QML_FOREIGNラッパーを使用して、静的ファクトリー関数を定義することが可能である。<br> <syntaxhighlight lang="c++"> struct SingletonForeign { Q_GADGET QML_FOREIGN(MySingleton) QML_SINGLETON QML_NAMED_ELEMENT(MySingleton) public: static MySingleton *create(QQmlEngine *, QJSEngine *engine) { MySingleton *result = nullptr; // 何らかのカスタムコンストラクタやファクトリを用いてインスタンスを生成する // QMLエンジンは、最終的にそのインスタンスの所有権を取得および削除する return result; } }; </syntaxhighlight> <br> もし、特定のシングルトンオブジェクトを提供する場合、その生成はコントロールできないが、静的ファクトリー関数からそれを返すことができる。<br> これは、<code>qmlRegisterSingletonInstance</code>関数の代わりとなるものである。 <syntaxhighlight lang="c++"> qmlRegisterSingletonInstance("MyModule", 1, 0, "MySingleton", myObject); </syntaxhighlight> <br> もし、上記のqmlRegisterSingletonInstance("MyModule", 1, 0, "MySingleton", myObject)を記述している場合、MyObjectがMySingleton *型であれば、<br> 代わりに、以下の例のようにクラスを定義することができる。<br> <br> これは、既存のクラスであるMySingletonクラスは、MySingletonというQMLのシングルトンであることが宣言される。<br> s_singletonInstanceメンバを設定することにより、使用する前にいつでもそのインスタンスを指定することができるため、MySingletonクラス自身を変更する必要はない。<br> <syntaxhighlight lang="c++"> struct SingletonForeign { Q_GADGET QML_FOREIGN(MySingleton) QML_SINGLETON QML_NAMED_ELEMENT(MySingleton) public: // Initialize this using myObject where you would previously // call qmlRegisterSingletonInstance(). inline static MySingleton *s_singletonInstance = nullptr; static MySingleton *create(QQmlEngine *, QJSEngine *engine) { // The instance has to exist before it is used. We cannot replace it. Q_ASSERT(s_singletonInstance); // The engine has to have the same thread affinity as the singleton. Q_ASSERT(engine->thread() == s_singletonInstance->thread()); // There can only be one engine accessing the singleton. if (s_engine) Q_ASSERT(engine == s_engine); else s_engine = engine; // Explicitly specify C++ ownership so that the engine doesn't delete // the instance. QJSEngine::setObjectOwnership(s_singletonInstance, QJSEngine::CppOwnership); return s_singletonInstance; } private: inline static QJSEngine *s_engine = nullptr; }; </syntaxhighlight> <br> <u>※注意</u><br> <u>このパターンは、シングルトンが複数のQMLエンジンからアクセスされる場合、または、アクセスするQMLエンジンがシングルトンオブジェクト自身と異なるスレッドアフィニティを持っている場合は動作しない。</u><br> <u>上記で示したように、<code>create()</code>メソッドのパラメータでエンジンのIDやスレッドアフィニティを確認することにより、アサーションすることができる。</u><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__ [[カテゴリ:Qt]]
QMLの基礎 - シングルトン
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse