はじめに
プラグインに対応したアプリケーションなら、本体側の開発と、拡張機能部分の開発を分離できるのでは、と思いQtを使ったプラグイン対応アプリケーションの作成方法を調べたのでまとめます。
プラグインには、ダイナミックプラグインとスタティックプラグインの2種類があります。
この記事では、スタティックプラグインの作り方をまとめます。
大まかな流れ
プラグインを受け入れる側(本体)の準備
- プラグイン用のインターフェースクラスを作成する
-
Q_DECLARE_INTERFACE
マクロで、インターフェースクラスと識別用のIDを関連づける -
Q_IMPORT_PLUGIN
マクロで、インターフェースの実装クラス(プラグイン側)を、スタティックプラグインとしてインポートできるようにする - .proファイルに
LIBS += <検索ディレクトリ> <リンクするプラグイン名>
を追加して、プラグインをリンクする -
QPluginLoader::staticInstances()
を使って、指定の場所からプラグインをロードして使う
プラグイン側の準備
- プロジェクトを、スタティックプラグインとしてビルドできるように、.proを加筆・修正
- プラグインのメタデータ用にjsonファイルを作成する。中身はカラでも良い
- 本体側で定義した、プラグイン用のインターフェースクラスの実装クラスを作成
- この実装クラスに、
Q_PLUGIN_METADATA
、Q_INTERFACES
マクロを記述して、プラグインとしてエクスポートできるようにする
スタティックプラグインを使ったアプリケーションを作成する
アプリケーションの概要
ここでは、先に示した大まか流れに沿って、お絵描きアプリケーションを作っていきます。
お絵描きアプリケーションといっても、ペンツールと消しゴムツールしかない、極シンプルなものです。
この二つのツールを、スタティックプラグインとして作成します。
プラグインを受け入れる側(本体)の準備(SimplePaint)
プラグイン用のインターフェースクラスを作成する
# include <QtPlugin>
class ITool
{
...
};
# define ITool_iid "jp.tatsuteb.SimplePaint.ITool.v1"
Q_DECLARE_INTERFACE(ITool, ITool_iid)
プラグイン用のインターフェースクラスを作成したら、クラス定義の外側に Q_DECLARE_INTERFACE(Interface ClassName, ID) を記述します。QtPluginのインクルードも忘れずに行ってください。
ロードしたプラグインが、このインターフェースの実装を持っているかどうか照会するのに使われるようです。
インターフェースの実装クラスを、スタティックプラグインとしてインポートできるようにする
# include <QtPlugin>
Q_IMPORT_PLUGIN(PenToolPlugin)
Q_IMPORT_PLUGIN(EraserToolPlugin)
int main(int argc, char *argv[])
{
...
}
Q_IMPORT_PLUGIN(実装クラス名)を記述することで、スタティックプラグインを常に本体側で使えるようにします。
.proファイルを編集して、プラグインをリンクできるようにする
LIBS += -L$$PWD/plugins/ -lsp_pentool -lsp_erasertool
if(!debug_and_release|build_pass):CONFIG(debug, debug|release) {
mac:LIBS = $$member(LIBS, 0) $$member(LIBS, 1)_debug $$member(LIBS, 2)_debug
win32:LIBS = $$member(LIBS, 0) $$member(LIBS, 1)d $$member(LIBS, 2)d
}
LIBSで、プラグインが格納されているディレクトリと、プラグイン名を指定します。-Lは検索するディレクトリ、-lはライブラリ名を示します。
ここでは、プラグイン名は「sp_pentool(ペンツール)」と「sp_erasertool(消しゴムツール)」にする予定なので、上記のような記述になっています。
ifの中でやっていることは、デバッグビルドとリリースビルドで、プラグインファイル名に_debugや_dがついたりつかなかったりするので、ビルドのタイプに応じてリンクするファイル名を変えています。
$$member(LIBS, 0) は LIBSの0番目の値を取得する、という意味になります。
QPluginLoader::staticInstances() を使って、指定の場所からプラグインをロードして使う
# include <QPluginLoader>
# include "ITool.h"
...
QList<ITool *> SimplePaint::loadToolPlugins()
{
QList<ITool *> tools;
for (QObject *plugin : QPluginLoader::staticInstances())
{
// qobject_castはキャストに失敗すると0を返す
auto tool = qobject_cast<ITool *>(plugin);
// ITool以外のプラグインは無視
if (!tool)
{
continue;
}
tools << tool;
}
return tools;
}
QPluginLoaderは、LIBSで指定したスタティックプラグインを保持しているので、QPluginLoader::staticInstances()を使って、QObjectListとして取得します。QPluginLoaderのインクルードも忘れずに行ってください。
取得したプラグインはqobject_cast<*インターフェースクラス* *>(plugin)で、適切なクラスにキャストして使用します。キャストに失敗した場合は、ゼロが返ってきます。
プラグインの準備(SimplePaintPenToolPlugin)
プロジェクトを、スタティックプラグインとしてビルドできるように、.proを加筆・修正
QT += core
TARGET = $$qtLibraryTarget(sp_pentool)
TEMPLATE = lib
CONFIG += plugin static
INCLUDEPATH += $$PWD/../../SimplePaint/
...
DESTDIR = $$PWD/../../SimplePaint/plugins
TARGET に出力するプラグインの名前を指定します。$$qtLibraryTarget(ファイル名)とすることで、デバッグビルドの時にmacでは_debug、Windowsでは_dがファイル名に付加されるようです。
TEMPLATEにはlibを指定します。
今回は、スタティックプラグインとしてビルドしたいので、CONFIGにはpluginとstaticを追加します。
INCLUDEPATHには、本体側に作成したインターフェースクラスが格納されているディレクトリを追加します。これにより、ITool.hがインクルードできるようになります。
DESTDIRには、プラグインを出力するディレクトリへのパスを追加します。
プラグインのメタデータ用にjsonファイルを作成する
{}
プラグインのメタデータを記述したjsonファイルを作成します。今回は、中身はカラです。
本体側で定義した、プラグイン用インターフェースクラスの実装クラスを作成
# include <QObject>
# include <QtPlugin>
# include "ITool.h"
class PenToolPlugin : public QObject, public ITool
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "jp.tatsuteb.SimplePaint.ITool.v1" FILE "PenTool.json")
Q_INTERFACES(ITool)
public:
...
};
Q_OBJECTの後に、Q_PLUGIN_METADATA(IID 本体側に作成したIDと同じもの FILE メタデータ用 jsonファイル)マクロを記述します。このプラグインをエクスポートできるようにします。
QtObjectとQtPluginのインクルードも忘れずに行います。
Q_INTERFACES(インターフェースクラス名)を記述して、基底クラスはプラグインインターフェースであることをmocに伝えます。
このマクロによりqobject_cast()でキャストできるようになるようです。
サンプル
GitHubに、線を描いて消すだけのサンプルを上げておきました。
GitHub - SimplePaint
各ツール(プラグイン)は、ToolPluginProjectsのサブプロジェクトになっています。
参考
主に、公式ドキュメントの「Plug & Paint」を参考にしました。こちらには、ダイナミックプラグインの作り方も詳しく書いてあります。
本体
Qt Documentation - Plug & Paint Example
スタティックプラグイン
Qt Documentation - Plug & Paint Basic Tools Example
ダイナミックプラグイン
Qt Documentation - Plug & Paint Extra Filters Example
おわりに
今回は、スタティックプラグインを使って、簡易お絵描きアプリのツールを実装しました。ところどころ、理解が足りていない部分があるので、今後修正するかもしれません…
スタティックプラグインなので、本体も一緒にビルドする必要があるわけですが、ダイナミックプラグインとして実装すれば、本体をビルドすることなくツールを開発できるので、その方が良かったのかな…と思いました。