QMLの基礎 - ライブラリ

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

Qt Creatorにおいて、QMLを使用した静的ライブラリおよび動的ライブラリを作成して、他のQtプロジェクトにリンクする手順を記載する。


動的ライブラリ

動的ライブラリの作成

  1. QT Creatorのメイン画面から、[ファイル]メニューバー - [ファイル / プロジェクトの新規作成]を選択する。
  2. [新しいファイルまたはプロジェクト]画面左にある[ライブラリ]から[C++ Library]を選択する。
  3. [C++ Library]画面が開くので、以下の項目を設定する。
    • プロジェクト名やビルドシステム等を任意のものに設定する。
    • [プロジェクトの詳細定義]では、以下の手順で設定する。
      [Type:]は、[Shared Library]
      [Qt module:]は、Qtクラスを使用しない場合は[None]、Qtクラスを使用する場合は[Qt Core]、
      画面の作成が伴う場合は[Qt Gui]、ウィジェットの作成が伴う場合は[Widget]を選択する。
      [クラス名:]、[ソースファイル名:]、[ヘッダ名:]は任意のものに設定する。
    • [Translation File]や[キットの選択]は任意のものに設定する。


以下の例では、動的ライブラリとしてQMLのWindowコンポーネントを作成している。

 // LibSample_global.hファイル
 
 #ifndef LIBSAMPLE_GLOBAL_H
 #define LIBSAMPLE_GLOBAL_H
 
 #if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
    #define Q_DECL_EXPORT __declspec(dllexport)
    #define Q_DECL_IMPORT __declspec(dllimport)
 #else
    #define Q_DECL_EXPORT __attribute__((visibility("default")))
    #define Q_DECL_IMPORT __attribute__((visibility("default")))
 #endif
 
 #if defined(LIBSAMPLE_LIBRARY)
    #define LIBSAMPLE_EXPORT Q_DECL_EXPORT
 #else
    #define LIBSAMPLE_EXPORT Q_DECL_IMPORT
 #endif
 
 #endif // LIBSAMPLE_GLOBAL_H


 // LibSample.h
 
 #ifndef LIBSAMPLE_H
 #define LIBSAMPLE_H
 
 #include <QObject>
 #include <QString>
 #include <QQmlComponent>
 #include <QtQml/qqmlengine.h>
 #include "hoge_global.h"
 
 class LIBSAMPLE_EXPORT LibSample : public QObject
 {
    Q_OBJECT
 
 public:
    explicit LibSample(QQmlEngine *engine, QObject *parent = nullptr);
    Q_INVOKABLE void showQmlWindow();
 
 private:
    QQmlEngine *m_engine;
 };
 
 #endif // LIBSAMPLE_H


 // LibSample.cpp
 
 #include <QQmlComponent>
 #include <QtQuick/QQuickWindow>
 #include "LibSample.h"
  
 LibSample::LibSample(QQmlEngine *engine, QObject *parent) : QObject(parent), m_engine(engine)
 {
    qDebug() << "LibSample lib";
 }
 
 void LibSample::showQmlWindow()
 {
    QQmlComponent component(m_engine, "qrc:/Hoge/Hoge.qml");
 
    QObject *object = component.create();
    if (object) {
       QQuickWindow *window = qobject_cast<QQuickWindow*>(object);
       if (window) {
          window->show();
       }
    }
 }


動的ライブラリのプロジェクトをビルドして、debugディレクトリまたはreleaseディレクトリにライブラリが生成される。

※注意
Windowsの場合、.dllファイルと.aファイルが生成されるため、.aファイルの.a拡張子を.lib拡張子に変更する必要がある。

動的ライブラリのリンク

動的ライブラリをQtプロジェクトにリンクするには、以下の手順を行う。

  1. Qt Creatorのメイン画面左の[プロジェクト]ペインから、プロジェクト名を右クリックして、[ライブラリを追加]を選択する。
  2. [ライブラリの追加]画面が開くので、以下の項目を設定する。
    • [ライブラリの種類]画面は、[外部ライブラリ]を選択する。
    • [外部ライブラリ]画面は、ライブラリのリンク方法とライブラリのパスを選択する。
      [ライブラリファイル:]項目は、動的ライブラリファイルのパスを入力する。(動的ライブラリの.soファイルまたは.aファイルを指定)
      [インクルードパス:]項目は、動的ライブラリのヘッダファイルが存在するパスを入力する。
      [リンク方法]は、[ダイナミック]を選択する。
  3. これにより、動的ライブラリとして、Qtプロジェクトにリンクすることができる。
    Qtプロジェクトのビルドは不要である。


次に、動的ライブラリの機能を使用するため、動的ライブラリのヘッダファイルをインクルードする。

  1. Qt Creatorのメイン画面左の[プロジェクト]ペインから、プロジェクト名を右クリックして、[既存のファイルの追加...]を選択する。
  2. ファイル選択ダイアログから、動的ライブラリのヘッダファイルを選択する。
    ヘッダファイルをインクルードすることにより、動的ライブラリの機能が使用できるようになる。


※注意
ただし、動的ライブラリはQT Creatorから独立して実行されるため、以下に示す手順を行う必要がある。

  • 動的ライブラリのパスを直接指定する場合
    まず、CMakeLists.txtファイルにおいて、target_link_librariesコマンドを使用して、.soファイルのパスを指定する。
    次に、target_include_directoriesコマンドを使用して、動的ライブラリが定義されているヘッダファイルが存在するディレクトリのパスを指定する。

  • find_packageコマンドを使用する場合
    また、動的ライブラリに.pcファイルが存在する場合、find_packageコマンドを使用してもよい。
    find_packageコマンドを使用する場合でも、
    target_include_directoriesコマンド (変数${xxx_INCLUDE_DIRS})、および、target_link_librariesコマンド (変数${xxx_LIBRARIES})を使用する必要がある。
 # CMakeLists.txtファイル
 
 # ...略
 
 project(exeApp LANGUAGES CXX)
 
 set(CMAKE_AUTOMOC ON)
  
 # ...略
 
 # ライブラリへのパスを指定
 target_link_libraries(exeSample PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/../LibSample/${CMAKE_BUILD_TYPE}/libsample.so
 )
 
 target_include_directories(exeSample PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/../LibSample
 )
 
 # ...略


 #include <QGuiApplication>
 #include <QQmlApplicationEngine>
 #include <QQmlContext>
 #include "LibSample.h"
 
 #include "app_environment.h"
 #include "import_qml_components_plugins.h"
 #include "import_qml_plugins.h"
 
 int main(int argc, char *argv[])
 {
    set_qt_environment();
 
    QGuiApplication app(argc, argv);
 
    QQmlApplicationEngine engine;
    const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs);
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
                     [url](QObject *obj, const QUrl &objUrl) {
                        if (!obj && url == objUrl)
                        QCoreApplication::exit(-1);
                     },
                     Qt::QueuedConnection);
 
    LibSample libSample(&engine);
    engine.rootContext()->setContextProperty("libSample", &libSample);
 
    engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
    engine.addImportPath(":/");
 
    engine.load(url);
 
    if (engine.rootObjects().isEmpty()) {
       return -1;
    }
 
    return app.exec();
 }


 // main.qmlファイル
 
 import QtQuick
 import QtQuick.Window
 
 Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("QML from Library")
 
    Text {
       anchors.centerIn: parent
       text: libSample.getQmlSource()
       wrapMode: Text.WordWrap
    }
 }


動的ライブラリの明示的リンク

Qtには、動的ライブラリの関数を簡単に使用するために、QLibraryクラスが用意されている。

明示的リンクを使用して、動的ライブラリの関数を呼び出す手順は、以下の通りである。

  1. 動的ライブラリのメソッドにおいて、C言語のグローバル関数 (Cスタイルのインターフェース) として定義することにより、ライブラリの動的ロードを容易にする。
  2. 実行ファイル側において、QLibraryクラスを使用して動的にライブラリを読み込み、動的ライブラリのクラスのインスタンスを生成する。
  3. 生成したインスタンスをQMLコンテキストにセットして、QMLファイルから動的ライブラリのクラスのメソッドを呼び出せるようにする。
  4. 実行ファイル側のQMLファイルから動的ライブラリのオブジェクトのメソッドを呼び出して、動的ライブラリ内のQMLファイルを表示する。


以下の構成を持つ2つのプロジェクトを作成したとする。

  • プロジェクト構成
    • 動的ライブラリ
      Utils
    • 実行ファイル
      Moc


まず、Utilsプロジェクトにおいて、関数のみを定義したUtils.cppファイルを作成する。

 // Utils_global.h
 
 #ifndef UTILS_GLOBAL_H
 #define UTILS_GLOBAL_H
 
 #include <QtCore/qglobal.h>
 
 #if defined(Q_OS_WIN)   // Windows
    #if defined(UTILS_LIBRARY)
       #define UTILS_EXPORT __declspec(dllexport)
    #else
       #define UTILS_EXPORT
    #endif
 #elif defined(Q_OS_LINUX)  // Linux
    #if defined(UTILS_LIBRARY)
       #define UTILS_EXPORT Q_DECL_EXPORT
    #else
       #define UTILS_EXPORT Q_DECL_IMPORT
    #endif
 #endif
 
 #endif // UTILS_GLOBAL_H


 // Utils.cpp
 
 #include "Utils_global.h"
 #include <iostream>
 
 extern "C" UTILS_EXPORT void SampleHello(int count)
 {
    while(--count)
    {
       fprintf(stdout, "Hello!!");
    }
 }


次に、Mocプロジェクト(動的ライブラリを使用する側)から、動的ライブラリの関数を呼び出す。

QLibraryクラスから呼び出す関数は、必ず、C言語のグローバル関数として定義すること。
これは、マングリングしていない元の名前から関数を呼び出すためである。

※注意
Windowsでは、動的ライブラリから関数を呼び出すには、__declspec(dllexport)を関数の前に付加すること。

QLibraryクラスのコンストラクタには、.so拡張子または.dll拡張子を付加しないライブラリ名を渡す。
動的ライブラリをリンクするには、loadメソッドを実行する。
次に、resolveメソッドを使用して呼び出す関数名を渡して、関数が存在する場合は関数ポインタが返る。
関数名や型が間違っている場合は、0が返る。

もし、関数内でグローバル変数の値が変更された場合、次の関数を呼び出した時でも、グローバル変数の値を保持し続ける。

 // main.cpp
 
 #include <QApplication>
 #include <QLibrary>

 using SampleFunc = void (*)(int);
 
 int main(int argc, char * argv[])
 {
    QApplication a(argc, argv);
 
    // 動的ライブラリをリンクする
    QLibrary lib("Utils");
    lib.load();
 
    // 動的ライブラリの関数を取得する
    SampleFunc Func =  (SampleFunc)lib.resolve("SampleHello");
 
    if(Func != 0)
    {
       Func(10);
    }
 
    return a.exec();
 }