Qt プラグインの作成方法

Qtはプラグインを作成するための2つのAPIを提供しています:

  • Qt にはプラグインを作成するための API が 2 つ用意されています。高水準 API は Qt 自体を拡張するためのもので、カスタムデータベースドライバ、画像フォーマット、テキストコーデック、カスタムスタイルなどがあります。
  • Qt アプリケーションを拡張するための低レベル API です。

例えば、QStyle のサブクラスを作成し、Qt アプリケーションに動的にロードさせたい場合は、上位 API を使用します。

上位APIは下位APIの上に構築されているため、いくつかの問題は両方に共通します。

Qt Widgets Designerで使用するプラグインを提供したい場合は、Creating Custom Widget Pluginsを参照してください。

高レベル API:Qt 拡張機能の記述

Qt自身を拡張するプラグインを作成するには、適切なプラグイン基本クラスをサブクラス化し、いくつかの関数を実装し、マクロを追加します。

プラグインの基本クラスはいくつかあります。派生プラグインは、デフォルトでは標準プラグインディレクトリのサブディレクトリに格納されます。プラグインが適切なディレクトリに格納されていない場合、Qtはプラグインを見つけられません。

以下の表は、プラグインの基本クラスをまとめたものです。いくつかのクラスはプライベートなので、ドキュメント化されていません。それらを使用することはできますが、それ以降の Qt バージョンとの互換性は約束されていません。

基本クラスディレクトリ名Qt モジュールキー 大文字小文字の区別
QAccessibleBridgePluginaccessiblebridgeQt GUI大文字小文字を区別
QImageIOPluginimageformatsQt GUI大文字小文字を区別
QPictureFormatPlugin (廃止予定)pictureformatsQt GUI大文字小文字を区別
QBearerEnginePluginbearerQt ネットワーク大文字小文字を区別する
QPlatformInputContextPluginplatforminputcontextsQt プラットフォーム抽象化大文字小文字を区別しない
QPlatformIntegrationPluginplatformsQt プラットフォーム抽象化大文字小文字を区別しない
QPlatformThemePluginplatformthemesQt プラットフォーム抽象化大文字小文字を区別しない
QPlatformPrinterSupportPluginprintsupportQt 印刷サポート大文字小文字を区別しない
QSGContextPluginscenegraphQt クイック大文字小文字を区別
QSqlDriverPluginsqldriversQt SQL大文字小文字を区別
QIconEnginePluginiconenginesQt SVG大文字小文字を区別しない
QAccessiblePluginaccessibleQt ウィジェット大文字小文字を区別
QStylePluginstylesQt ウィジェット大文字小文字を区別しない

JsonViewer という新しいドキュメント・ビューア・クラスをプラグインとして利用したい場合、そのクラスは次のように定義する必要があります (jsonviewer.h):

class JsonViewer : public ViewerInterface
{
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0" FILE "jsonviewer.json")
    Q_INTERFACES(ViewerInterface)
public:
    JsonViewer();
    ~JsonViewer() override;
private:
    bool openJsonFile();

    QTreeView *m_tree;
    QListWidget *m_toplevel = nullptr;
    QJsonDocument m_root;

    QPointer<QLineEdit> m_searchKey;
};

クラスの実装が.cpp ファイルにあることを確認してください:

JsonViewer::JsonViewer()
{
    connect(this, &AbstractViewer::uiInitialized, this, &JsonViewer::setupJsonUi);
}

void JsonViewer::init(QFile *file, QWidget *parent, QMainWindow *mainWindow)
{
    AbstractViewer::init(file, new QTreeView(parent), mainWindow);
    m_tree = qobject_cast<QTreeView *>(widget());
}

さらに、ほとんどのプラグインでは、プラグインを説明するメタデータを含むjsonファイル(jsonviewer.json)が必要です。ドキュメント・ビューア・プラグインの場合は、単にビューア・プラグインの名前が含まれます。

{ "Keys": [ "jsonviewer" ] }

jsonファイルで提供する必要がある情報のタイプは、プラグインに依存します。ファイルに含める必要がある情報の詳細については、クラスのドキュメントを参照してください。

データベースドライバ、画像フォーマット、テキストコーデック、その他ほとんどのプラグインでは、明示的にオブジェクトを作成する必要はありません。Qt が必要に応じてオブジェクトを見つけて作成します。

プラグインクラスは、追加関数を実装する必要があるかもしれません。プラグインの種類ごとに再実装が必要な仮想関数の詳細については、クラスのドキュメントを参照してください。

ドキュメント・ビューア・デモでは、構造化されたファイルの内容を表示するプラグインの実装方法を示しています。そのため、各プラグインは仮想関数を再実装します。

  • プラグインを識別する
  • サポートしているMIMEタイプを返す
  • 表示するコンテンツがあるかどうかと
  • コンテンツの表示方法
    QString viewerName() const override { return QLatin1StringView(staticMetaObject.className()); };
    QStringList supportedMimeTypes() const override;
    bool hasContent() const override;
    bool supportsOverview() const override { return true; }

低レベル API:Qt アプリケーションの拡張

Qt 自身に加えて、Qt アプリケーションはプラグインによって拡張することができます。そのためには、QPluginLoader を使って、アプリケーションがプラグインを検出し、ロードする必要があります。その中で、プラグインは任意の機能を提供することができ、データベースドライバ、画像フォーマット、テキストコーデック、スタイル、その他 Qt の機能を拡張するタイプのプラグインに限定されません。

プラグインによってアプリケーションを拡張可能にするには、次のような手順が必要です:

  1. プラグインとやり取りするためのインターフェース(純粋な仮想関数のみを持つクラス)を定義する。
  2. Q_DECLARE_INTERFACE ()マクロを使用して、Qtのメタオブジェクトシステムにインターフェイスを伝えます。
  3. アプリケーションでQPluginLoader を使ってプラグインをロードします。
  4. qobject_cast() を使って、プラグインが与えられたインターフェイスを実装しているかどうかをテストする。

プラグインを作成するには、以下のステップを踏みます:

  1. QObject およびプラグインが提供したいインターフェイスを継承するプラグインクラスを宣言する。
  2. Q_INTERFACES ()マクロを使用して、Qtのメタオブジェクトシステムにインターフェイスを伝えます。
  3. Q_PLUGIN_METADATA() マクロを使ってプラグインをエクスポートします。

例えば、インターフェイスクラスの定義です:

class ViewerInterface : public AbstractViewer
{
public:
    virtual ~ViewerInterface() = default;
};

これがインターフェース宣言です:

#define ViewerInterface_iid "org.qt-project.Qt.Examples.DocumentViewer.ViewerInterface/1.0"
Q_DECLARE_INTERFACE(ViewerInterface, ViewerInterface_iid)

Qt Widgets Designer特有の問題については、Creating Custom Widgets for Qt Widgets Designerも参照してください。

プラグインの検索

プラグインは標準のプラグイン・サブディレクトリに格納されているため、Qtアプリケーションはどのプラグインが利用可能かを自動的に知ることができます。プラグインは標準のプラグイン・サブディレクトリに格納されているため、Qtアプリケーションは自動的にプラグインを知ることができます。

開発中、プラグイン用のディレクトリはQTDIR/plugins (QTDIR は Qt がインストールされているディレクトリ) で、プラグインの種類ごとにサブディレクトリが用意されています。例えば、styles です。アプリケーションでプラグインを使用したいが、標準のプラグインパスを使用したくない場合は、インストールプロセスでプラグインに使用するパスを決定し、アプリケーションの実行時に読み込めるように、例えばQSettings を使用してパスを保存します。アプリケーションは、このパスを使用してQCoreApplication::addLibraryPath() を呼び出すことができ、プラグインはアプリケーションから利用できるようになります。パスの最後の部分(たとえばstyles )は変更できないことに注意してください。

プラグインをロード可能にしたい場合は、アプリケーションの下にサブディレクトリを作成し、その中にプラグインを配置するという方法があります。Qt に付属しているプラグイン(plugins ディレクトリにあるもの)を配布する場合は、プラグインがあるplugins 以下のサブディレクトリをアプリケーションのルートフォルダにコピーする必要があります(つまり、plugins ディレクトリを含めないでください)。

デプロイの詳細については、Qt アプリケーションのデプロイと プラグインのデプロイのドキュメントを参照してください。

静的プラグイン

プラグインをアプリケーションに含める最も柔軟な方法は、ダイナミックライブラリにコンパイルすることです。

プラグインはアプリケーションに静的にリンクすることができます。Qt の静的バージョンをビルドする場合、これが Qt の定義済みプラグインを含めるための唯一の選択肢です。静的なプラグインを使用すると、デプロイ時にエラーが発生しにくくなりますが、アプリケーションを完全に再構築して再配布しなければ、プラグインから機能を追加できないという欠点があります。

CMake と qmake は、使用する Qt モジュールで一般的に必要とされるプラグインを自動的に追加しますが、より特殊なプラグインは手動で追加する必要があります。自動的に追加されるプラグインのデフォルトリストは、タイプごとにオーバーライドすることができます。

デフォルトは、すぐに使えるように最適化されていますが、不必要にアプリケーションを肥大化させる可能性があります。リンカー・コマンド・ラインを検査し、不要なプラグインを削除することを推奨します。

スタティック・プラグインが実際にリンクされ、インスタンス化されるためには、Q_IMPORT_PLUGIN ()マクロもアプリケーション・コードに必要ですが、これらはビルド・システムによって自動的に生成され、アプリケーション・プロジェクトに追加されます。

CMake で静的プラグインをインポートする

CMake プロジェクトでプラグインを静的にリンクするには、qt_import_pluginsコマンドを呼び出す必要があります。

例えば、Linuxlibinput プラグインはデフォルトではインポートされません。次のコマンドでインポートされます:

qt_import_plugins(myapp INCLUDE Qt::QLibInputPlugin)

デフォルトのQtプラットフォーム適応プラグインの代わりに、最小限のプラットフォーム統合プラグインをリンクするには、次のコマンドを使用します:

qt_import_plugins(myapp
    INCLUDE_BY_TYPE platforms Qt::MinimalIntegrationPlugin
)

もう1つの典型的な使用例は、特定のimageformats プラグインのセットだけをリンクすることです:

qt_import_plugins(myapp
    INCLUDE_BY_TYPE imageformats Qt::QJpegPlugin Qt::QGifPlugin
)

imageformats プラグインをリンクしないようにするには、次のようにします:

qt_import_plugins(myapp
    EXCLUDE_BY_TYPE imageformats
)

デフォルト・プラグインの追加をオフにしたい場合は、qt_import_pluginsNO_DEFAULT オプションを使用します。

qmake での静的プラグインのインポート

qmake プロジェクトでは、QTPLUGIN を使って必要なプラグインをビルドに追加する必要があります:

QTPLUGIN += qlibinputplugin

例えば、デフォルトのQtプラットフォーム適応プラグインの代わりにminimalプラグインをリンクするには、次のようにします:

QTPLUGIN.platforms = qminimal

デフォルトの Qt Platform Adaptation プラグインも最小限の QPA プラグインも自動的にリンクさせたくない場合は、次のように使用します:

QTPLUGIN.platforms = -

QTPLUGIN に追加されたすべてのプラグインを自動的にリンクさせたくない場合は、CONFIG 変数からimport_plugins を削除します:

CONFIG -= import_plugins

静的プラグインの作成

以下の手順で独自のスタティック・プラグインを作成することも可能です:

  1. CMakeLists.txtqt_add_pluginコマンドにSTATIC オプションを渡します。qmake プロジェクトの場合、プラグインの.pro ファイルにCONFIG += static を追加します。
  2. アプリケーションでQ_IMPORT_PLUGIN() マクロを使用します。
  3. プラグインが qrc ファイルを同梱している場合は、アプリケーションでQ_INIT_RESOURCE() マクロを使用します。
  4. CMakeLists.txt または.pro ファイルのLIBStarget_link_libraries を使用して、アプリケーションとプラグインライブラリをリンクします。

この方法の詳細については、Plug & Paint の例と関連するBasic Toolsプラグインを参照してください。

注意: プラグインのビルドにCMakeやqmakeを使用していない場合は、QT_STATICPLUGIN プリプロセッサマクロが定義されていることを確認する必要があります。

プラグインのロード

プラグインの種類(静的または共有)とオペレーティングシステムは、プラグインを検索してロードするための特定のアプローチを必要とします。プラグインをロードするための抽象化を実装しておくと便利です。

void ViewerFactory::loadViewerPlugins()
{
    if (!m_viewers.isEmpty())
        return;

QPluginLoader::staticInstances() は、静的にリンクされた各プラグインへのポインタを持つQObjectList を返します。

    // Load static plugins
    const QObjectList &staticPlugins = QPluginLoader::staticInstances();
    for (auto *plugin : staticPlugins)
        addViewer(plugin);

共有プラグインはデプロイメント・ディレクトリに存在し、OS固有の処理が必要になる場合があります。

    // Load shared plugins
    QDir pluginsDir = QDir(QApplication::applicationDirPath());

#if defined(Q_OS_WINDOWS)
    pluginsDir.cd("app"_L1);
#elif defined(Q_OS_DARWIN)
    if (pluginsDir.dirName() == "MacOS"_L1) {
        pluginsDir.cdUp();
        pluginsDir.cdUp();
        pluginsDir.cdUp();
    }
#endif
    const auto entryList = pluginsDir.entryList(QDir::Files);
    for (const QString &fileName : entryList) {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        if (plugin)
            addViewer(plugin);
#if 0
        else
            qDebug() << loader.errorString();
#endif
    }
}

プラグインのデプロイとデバッグ

プラグインのデプロイ」ドキュメントでは、プラグインをアプリケーションにデプロイするプロセスと、問題が発生した場合のデバッグについて説明します。

QPluginLoaderQLibraryも参照してください

©2024 The Qt Company Ltd. 本書に含まれるドキュメントの著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。