概要
Qt Creatorにおいて、QMLを使用した静的ライブラリおよび動的ライブラリを作成して、他のQtプロジェクトにリンクする手順を記載する。
動的ライブラリ
動的ライブラリの作成
- QT Creatorのメイン画面から、[ファイル]メニューバー - [ファイル / プロジェクトの新規作成]を選択する。
- [新しいファイルまたはプロジェクト]画面左にある[ライブラリ]から[C++ Library]を選択する。
- [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プロジェクトにリンクするには、以下の手順を行う。
- Qt Creatorのメイン画面左の[プロジェクト]ペインから、プロジェクト名を右クリックして、[ライブラリを追加]を選択する。
- [ライブラリの追加]画面が開くので、以下の項目を設定する。
- [ライブラリの種類]画面は、[外部ライブラリ]を選択する。
- [外部ライブラリ]画面は、ライブラリのリンク方法とライブラリのパスを選択する。
- [ライブラリファイル:]項目は、動的ライブラリファイルのパスを入力する。(動的ライブラリの
.so
ファイルまたは.a
ファイルを指定) - [インクルードパス:]項目は、動的ライブラリのヘッダファイルが存在するパスを入力する。
- [リンク方法]は、[ダイナミック]を選択する。
- [ライブラリファイル:]項目は、動的ライブラリファイルのパスを入力する。(動的ライブラリの
- これにより、動的ライブラリとして、Qtプロジェクトにリンクすることができる。
Qtプロジェクトのビルドは不要である。
次に、動的ライブラリの機能を使用するため、動的ライブラリのヘッダファイルをインクルードする。
- Qt Creatorのメイン画面左の[プロジェクト]ペインから、プロジェクト名を右クリックして、[既存のファイルの追加...]を選択する。
- ファイル選択ダイアログから、動的ライブラリのヘッダファイルを選択する。
ヘッダファイルをインクルードすることにより、動的ライブラリの機能が使用できるようになる。
※注意
ただし、動的ライブラリはQT Creatorから独立して実行されるため、以下に示す手順を行う必要がある。
- 動的ライブラリのパスを直接指定する場合
- まず、CMakeLists.txtファイルにおいて、
target_link_libraries
コマンドを使用して、.soファイルのパスを指定する。 - 次に、
target_include_directories
コマンドを使用して、動的ライブラリが定義されているヘッダファイルが存在するディレクトリのパスを指定する。
- まず、CMakeLists.txtファイルにおいて、
find_package
コマンドを使用する場合- また、動的ライブラリに.pcファイルが存在する場合、
find_package
コマンドを使用してもよい。 find_package
コマンドを使用する場合でも、target_include_directories
コマンド (変数${xxx_INCLUDE_DIRS})、および、target_link_libraries
コマンド (変数${xxx_LIBRARIES})を使用する必要がある。
- また、動的ライブラリに.pcファイルが存在する場合、
# 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
クラスが用意されている。
明示的リンクを使用して、動的ライブラリの関数を呼び出す手順は、以下の通りである。
- 動的ライブラリのメソッドにおいて、C言語のグローバル関数 (Cスタイルのインターフェース) として定義することにより、ライブラリの動的ロードを容易にする。
- 実行ファイル側において、
QLibrary
クラスを使用して動的にライブラリを読み込み、動的ライブラリのクラスのインスタンスを生成する。 - 生成したインスタンスをQMLコンテキストにセットして、QMLファイルから動的ライブラリのクラスのメソッドを呼び出せるようにする。
- 実行ファイル側のQMLファイルから動的ライブラリのオブジェクトのメソッドを呼び出して、動的ライブラリ内のQMLファイルを表示する。
※注意
Windowsでは、動的ライブラリからメソッドを呼び出すには、__declspec(dllexport)
を関数の前に付加すること。
上記セクションにある動的ライブラリの変更点のみを、以下に示す。
まず、動的ライブラリのクラスおよびメソッドの変更点を記述する。
// LibSample.h (動的ライブラリ側)
#ifndef LIBSAMPLE_H
#define LIBSAMPLE_H
#include <QObject>
#include <QString>
#include <QQmlComponent>
#include <QtQml/qqmlengine.h>
#include "LibSample_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;
};
extern "C" {
LibSample* createLibSample();
void destroyLibSample(LibSample *instance);
}
#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();
}
}
}
LibSample* createLibSample()
{
return new LibSample();
}
void destroyLibSample(LibSample *instance)
{
delete instance;
}
次に、実行ファイル側 (動的ライブラリを使用する側) から動的ライブラリを読み込み、動的ライブラリのクラスのインスタンスを生成する。
生成したインスタンスをQMLコンテキストにセットして、QMLファイルから動的ライブラリのクラスのメソッドを呼び出す。
QLibrary
クラスから呼び出す関数は、必ず、C言語のグローバル関数として定義すること。
これは、マングリングしていない元の名前から関数を呼び出すためである。
QLibrary
クラスのコンストラクタには、.so
拡張子または.dll
拡張子を付加しないライブラリ名を渡す。
動的ライブラリをリンクするには、load
メソッドを実行する。
次に、resolve
メソッドを使用して呼び出す関数名を渡して、関数が存在する場合は関数ポインタが返る。
メソッド名や型が間違っている場合は、0
が返る。
もし、メソッド内でグローバル変数の値が変更された場合、次の関数を呼び出した時でも、グローバル変数の値を保持し続ける。
// main.cpp (実行ファイル側)
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QLibrary>
#include "LibSample.h"
using CreateLibSampleInstance = LibSample* (*)();
using DestroyLibSampleInstance = void (*)(LibSample*);
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(u"qrc:/main.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app,
[url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
QLibrary lib("LibSample");
if (lib.load()) {
CreateLibSampleInstance createInstance = (CreateLibSampleInstance) lib.resolve("createLibSample");
DestroyLibSampleInstance destroyInstance = (DestroyLibSampleInstance) lib.resolve("destroyLibSample");
if (createInstance && destroyInstance) {
LibSample *sample = createInstance();
engine.rootContext()->setContextProperty("libSample", sample);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [sample, destroyInstance]() {
destroyInstance(sample);
});
}
else {
qWarning() << "インスタンスメソッドの作成 / 破棄の解決に失敗";
}
}
else {
qWarning() << "動的ライブラリの読み込みに失敗";
}
return app.exec();
}